Setup
rm(list = ls(all.names = TRUE))
if(!require("pacman")) install.packages("pacman"); library(pacman)
pacman::p_load(
tidyverse,
scales,
janitor,
GCLr,
coin
)
knitr::opts_chunk$set(fig.width = 10)
knitr::opts_chunk$set(echo = TRUE)
.username = readLines("~/usr_pw.txt", n = 1) # LOKI username
.password = readLines("~/usr_pw.txt" , n = 2)[[2]] # LOKI password
Objective
The purpose of this notebook is to provide Wes Larson (NOAA ABL) with
adaptive run timing genotypes (9 loci) for AHRP chum samples. Filter out
plates undergoing QC crosschecks due to poor genotyping success. Wes is
interested in representation across years, less across multiple streams.
He wants to see how the patterns hold up over time.
Steps
- import all existing genotypes for CM064-CM070
- filter out untrustworthy plates that need QC cross checks
- standard GT-seq genotype QA (missing, heterozygosity,
duplicate)
- join sample date and spawning state <“../OceanAK/AHRP Salmon
Biological Data 20240105_122404.368459_with_spawning_state.csv”>
- filter for 9 adaptive run timing loci
- export genotypes with date/spawning state
Background
An adaptive marker panel associated with run timing variation in chum
salmon was developed by NOAA ABL and used to genotype two plates of
samples from the ADFG Alaska Hatchery Research Program (AHRP) study
being conducted within SE Alaska. AHRP samples (Fish Creek - Douglas
Island 2023 + Prospect Creek 2017/2018) were genotyped in August 2024
for 20 single nucleotide polymorphisms and weighted linear regressions
were performed to assess the association of different SNPs with run
timing (date of collection). The chum salmon adaptive marker panel
contains 20 loci spread among 5 chromosomes and was developed from
variation detected between Yukon River summer (Gisasa River) and fall
(Pelly River) run chum salmon with low coverage whole genome
sequencing.These populations spawn approximately two months and over
1500 river km apart. NOAA targeted the major peaks in differentiation
with particular interest in markers within the genes leucine rich repeat
containing nine (LRRC9; LG35) and estrogen receptor β (ESRB; LG29) as
they were associated with return timing in other salmonid species. Eight
SNPs (four on LG29 and four on LG35) appear to be associated with run
timing within these collections. The strength of association for AHRP
samples appears to vary by population. No markers on LGs 5, 23 or 15
appear to have a strong association with run timing within these
collections. Overall, the results from LG29 and LG35 are promising and
variation in these SNPs should be surveyed for a larger collection of
samples.
A total of 9 adaptive, run timing loci were added to the AHRP SEAK
Chum parentage panel:
- OkeV2_LG23_10546237
- OkeV2_LG29_25450752
- OkeV2_LG29_25455833
- OkeV2_LG29_25483595
- OkeV2_LG29_25515161
- OkeV2_LG35_28078867
- OkeV2_LG35_28128687
- OkeV2_LG35_28165076
- OkeV2_LG35_28167172
All AHRP Chum samples from Fish Creek - Douglas Island, Prospect
Creek, Admiralty Creek, and Sawmill Creek were genotyped for the
parentage panel at the ADF&G Gene Conservation Laboratory (GCL) in
Winter/Spring 2025. Due to poor tissue quality, there was a high
proportion of samples that failed to genotype and/or appear to be
contaminated (high heterozygosity), resulting in low QC power for
several plates. QC crosschecks are currently underway at the GCL in July
2025, however, Wes wants whatever genotypes we have on hand now so will
just filter out those plates.
Import Data
LocusControl
Create objects, create LocusControl for
Chum_AHRP_257_v1.3.0.
fs::dir_create("objects")
GCLr::create_locuscontrol(markersuite = "Chum_AHRP_257_v1.3.0",
username = .username,
password = .password)
GCLr::save_objects(objects = "LocusControl", path = "objects")
Hrmm, only 251 loci. Confirm that these are the same loci run in the
most recent AHRP Chum project CM069
probe_loci_CM069 <- readr::read_tsv(
file = "V:/Lab/Genotyping/SNP Projects/Chum/Project CM069 AHRP Parentage/Genotype Data Files/set2rr_CM069_outputs/probes.txt",
show_col_types = FALSE) %>%
dplyr::distinct(`#Locus_ID`) %>%
dplyr::pull()
all.equal(probe_loci_CM069, LocusControl$locusnames)
Yes, all are the same.
loci251 <- LocusControl$locusnames
GCLr::save_objects(objects = "loci251", path = "objects")
Genotypes
Read in genotypes by projec to get sillyvec.
rm(LocusControl)
# GCLr::loki2r_proj(project_name = paste0("CM0", 64:70), username = .username, password = .password) # comment out to avoid re-running
What collections?
(sillyvec <- sort(project_sillys))
Subset sillyvec to remove alevin collection
(sillyvec <- grep(pattern = "a", x = sillyvec, ignore.case = FALSE, value = TRUE, invert = TRUE))
Wipe out .gcl objects and re-read in using
loki2r.
GCLr::save_objects(objects = "sillyvec", path = "objects")
rm(list = objects(pattern = ".gcl"))
rm(LocusControl)
Read in genotypes.
GCLr::load_objects(path = "objects", pattern = "LocusControl")
GCLr::loki2r(sillyvec = sillyvec,
username = .username,
password = .password,
test_type = "GTSNP")
Only took 5.39 minutes to read everything in at the office!
Save raw genotypes (useful for working offsite and off VPN).
fs::dir_create("data/genotypes")
fs::dir_create("data/genotypes/raw")
# GCLr::save_sillys(sillyvec = sillyvec, path = "data/genotypes/raw", rds = TRUE) # hide so you don't overwrite on accident!
Re-load
Re-load, if necessary
GCLr::load_objects(path = "objects")
[1] "loci251" "LocusControl" "sillyvec"
GCLr::load_sillys(sillyvec = sillyvec, path = "data/genotypes/raw", rds = TRUE)
[1] "CMADMCR13.gcl" "CMADMCR14.gcl" "CMADMCR17.gcl" "CMADMCR18.gcl" "CMFISHCR13.gcl" "CMFISHCR14.gcl" "CMFISHCR17.gcl" "CMFISHCR18.gcl"
[9] "CMFISHCR19.gcl" "CMFISHCR20.gcl" "CMFISHCR21.gcl" "CMFISHCR22.gcl" "CMFISHCR23.gcl" "CMFISHCRT13.gcl" "CMFISHCRT21.gcl" "CMFISHCRT22.gcl"
[17] "CMFISHCRT23.gcl" "CMPROSCR13.gcl" "CMPROSCR14.gcl" "CMPROSCR17.gcl" "CMPROSCR18.gcl" "CMPROSCR19.gcl" "CMPROSCR20.gcl" "CMPROSCR21.gcl"
[25] "CMPROSCR22.gcl" "CMPROSCRT21.gcl" "CMPROSCRT22.gcl" "CMSAWCR13.gcl" "CMSAWCR14.gcl" "CMSAWCR15.gcl" "CMSAWCR17.gcl" "CMSAWCR18.gcl"
[33] "CMSAWCR19.gcl" "CMSAWCR20.gcl" "CMSAWCR21.gcl" "CMSAWCR22.gcl" "CMSAWCRT21.gcl" "CMSAWCRT22.gcl"
Metadata
Read in paired metadata from Salmon Biological Fact and Stream
Specimens, format similar to pws_pink_ped_dat.
NOTE I’m doing this in a bit of a hurry, so make sure
to proof read carefully if recycling this code!
(metadata <- readr::read_csv(file = "../OceanAK/AHRP Salmon Biological Data 20240105_122404.368459_with_spawning_state.csv"))
Rows: 27046 Columns: 25
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (10): silly_code, spawning_state, sex, tissue_type, dna_tray_code, location_code, sample_id, otolith_mark_present, otolith_mark_id, otolith_...
dbl (9): collection_id, fish_id, length_mm, dna_tray_well_code, sample_year, is_missing_paired_data_exists, well_has_more_than_one_sample, fw_a...
lgl (5): target_dna_tray_code, target_dna_tray_well_pos, target_container_array_type_id, container_array_type, determination_collection_id
date (1): sample_date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Modify
(
metadata <- metadata %>%
dplyr::select(-dplyr::contains("target")) %>%
dplyr::select(-dplyr::contains("determination")) %>%
dplyr::rename(date = sample_date,
length = length_mm,
silly = silly_code) %>%
tidyr::unite(col = "sample", dna_tray_code:dna_tray_well_code, sep = "_", remove = FALSE) %>%
tidyr::unite(col = "silly_source", c(silly, fish_id), sep = "_", remove = FALSE) %>%
dplyr::mutate(franz_id = stringr::str_c(stringr::str_sub(silly, 1, 2), # 1st letter of species name, 1st letter of stream name
stringr::str_sub(silly, -2, -1), # 2 digit sample year
"_",
stringr::str_pad(fish_id, width = 5, pad = "0", side = "left")), # 5 digit fishid using padded zeros up front)
year = lubridate::year(date),
stream = stringr::str_remove(string = location_code, pattern = " Creek"),
DOY = lubridate::yday(date), # day of year
sex = dplyr::case_when(
sex == "M" ~ "Male",
sex == "F" ~ "Female",
sex == "U" ~ NA_character_,
is.na(sex) ~ NA_character_),
origin = dplyr::case_when(
otolith_mark_present == "NO" ~ "Natural",
otolith_mark_present == "YES" ~ "Hatchery"
), # add origin variable
origin = base::factor(origin, levels = c("Natural", "Hatchery")), # make factor to ensure hatchery != red
spawning_state = dplyr::case_when(
spawning_state == "Unknown" ~ NA_character_,
TRUE ~ spawning_state
) # force Unknown to NA
) %>%
dplyr::select(
franz_id, # unique identifier for FRANz (e.g. PE16_00001, species, stream, year, GCL fish_id)
sample, # ultimate data key for AHRP, DWP barcode + DWP position
silly_source, # unique identifier for LOKI (GCL database)
stream, # 5 pedigree streams
year, # sample year (aka death year for FRANz)
origin, # hatchery or wild
# hatchery, # which hatchery
sex, # male, female, unknown
date, # sample date
DOY, # day of year (aka julian sample date)
length, # mideye to hypural plate length in mm
# intertidal, # sampling location above or below intertidal
# distance_mouth, # riverdist approximate sampling location from mouth in meters
spawning_state, # alive, pink gill, grey gill, rotting (aka index of stream life)
# pre_spawn, # TRUE/FALSE (added in 2016); if the eggs and milt do not easily release and carcass has completely full gonads
# partial_spawn, # TRUE/FALSE (added in 2015); fish is sampled (dead or alive) with more than a little eggs or milt
# preyed_upon, # TRUE/FALSE (added in 2015); gonads removed by predators
# stream_trib, # stream tributary (Gilmour, Paddy, and Stockdale have multiple)
# latitude, # sampling area (where fish were moved to prior to sampling, rough approximate of spawning location)
# longitude, # sampling area (where fish were moved to prior to sampling, rough approximate of spawning location)
otolith_mark_present, # otolith mark present (yes = hatchery, no = natural)
otolith_mark_id, # otolith hatchery mark
otolith_mark_status_code, # otolith read status code (reason for no read = overground, lost, etc.)
silly, # GCL collection code
fish_id, # GCL fish ID within a silly
dna_tray_code, # 10-digit DWP barcode
dna_tray_well_code, # 48 DWP position 1:48
# dna_tray_well_pos, # 48 DWP position A1:H6
# distance_tide, # riverdist approximate sampling location from maximum high tide
# riverdist_seg, # riverdist river segment
# riverdist_vert, # riverdist vertex
# riverdist_snapdist, # riverdist snapping distance (aka how far was sampling location lat/long from the stream polyline)
# high_tide # highest extent of high tide in meters from mouth, unique to each stream
fw_age,
sw_age
)
)
Sample Sizes
Verify that all genotyped samples have metadata.
geno_silly_source <- sapply(sillyvec, function(silly) {get(paste0(silly, ".gcl"))$SillySource}) %>% unlist()
table(geno_silly_source %in% metadata$silly_source)
FALSE TRUE
241 18569
Shoot, which samples do not have any metadata?
Ah, I bet they are the Fish Creek tag samples from 2013 that Tyler
Dann and Heather Liller collected!
setdiff(geno_silly_source, metadata$silly_source)
[1] "CMFISHCRT13_1" "CMFISHCRT13_2" "CMFISHCRT13_3" "CMFISHCRT13_4" "CMFISHCRT13_5" "CMFISHCRT13_6" "CMFISHCRT13_7" "CMFISHCRT13_8"
[9] "CMFISHCRT13_9" "CMFISHCRT13_10" "CMFISHCRT13_11" "CMFISHCRT13_12" "CMFISHCRT13_13" "CMFISHCRT13_14" "CMFISHCRT13_15" "CMFISHCRT13_16"
[17] "CMFISHCRT13_17" "CMFISHCRT13_18" "CMFISHCRT13_19" "CMFISHCRT13_20" "CMFISHCRT13_21" "CMFISHCRT13_22" "CMFISHCRT13_23" "CMFISHCRT13_24"
[25] "CMFISHCRT13_25" "CMFISHCRT13_26" "CMFISHCRT13_27" "CMFISHCRT13_28" "CMFISHCRT13_29" "CMFISHCRT13_30" "CMFISHCRT13_31" "CMFISHCRT13_32"
[33] "CMFISHCRT13_33" "CMFISHCRT13_34" "CMFISHCRT13_35" "CMFISHCRT13_36" "CMFISHCRT13_37" "CMFISHCRT13_38" "CMFISHCRT13_39" "CMFISHCRT13_40"
[41] "CMFISHCRT13_41" "CMFISHCRT13_42" "CMFISHCRT13_43" "CMFISHCRT13_44" "CMFISHCRT13_45" "CMFISHCRT13_46" "CMFISHCRT13_47" "CMFISHCRT13_48"
[49] "CMFISHCRT13_49" "CMFISHCRT13_50" "CMFISHCRT13_51" "CMFISHCRT13_52" "CMFISHCRT13_53" "CMFISHCRT13_54" "CMFISHCRT13_55" "CMFISHCRT13_56"
[57] "CMFISHCRT13_57" "CMFISHCRT13_58" "CMFISHCRT13_59" "CMFISHCRT13_60" "CMFISHCRT13_61" "CMFISHCRT13_62" "CMFISHCRT13_63" "CMFISHCRT13_64"
[65] "CMFISHCRT13_65" "CMFISHCRT13_66" "CMFISHCRT13_67" "CMFISHCRT13_68" "CMFISHCRT13_69" "CMFISHCRT13_70" "CMFISHCRT13_71" "CMFISHCRT13_72"
[73] "CMFISHCRT13_73" "CMFISHCRT13_74" "CMFISHCRT13_75" "CMFISHCRT13_76" "CMFISHCRT13_77" "CMFISHCRT13_78" "CMFISHCRT13_79" "CMFISHCRT13_80"
[81] "CMFISHCRT13_81" "CMFISHCRT13_82" "CMFISHCRT13_83" "CMFISHCRT13_84" "CMFISHCRT13_85" "CMFISHCRT13_86" "CMFISHCRT13_87" "CMFISHCRT13_88"
[89] "CMFISHCRT13_89" "CMFISHCRT13_90" "CMFISHCRT13_91" "CMFISHCRT13_92" "CMFISHCRT13_93" "CMFISHCRT13_94" "CMFISHCRT13_95" "CMFISHCRT13_96"
[97] "CMFISHCRT13_97" "CMFISHCRT13_98" "CMFISHCRT13_99" "CMFISHCRT13_100" "CMFISHCRT13_101" "CMFISHCRT13_102" "CMFISHCRT13_103" "CMFISHCRT13_104"
[105] "CMFISHCRT13_105" "CMFISHCRT13_106" "CMFISHCRT13_107" "CMFISHCRT13_108" "CMFISHCRT13_109" "CMFISHCRT13_110" "CMFISHCRT13_111" "CMFISHCRT13_112"
[113] "CMFISHCRT13_113" "CMFISHCRT13_114" "CMFISHCRT13_115" "CMFISHCRT13_116" "CMFISHCRT13_117" "CMFISHCRT13_118" "CMFISHCRT13_119" "CMFISHCRT13_120"
[121] "CMFISHCRT13_121" "CMFISHCRT13_122" "CMFISHCRT13_123" "CMFISHCRT13_124" "CMFISHCRT13_125" "CMFISHCRT13_126" "CMFISHCRT13_127" "CMFISHCRT13_128"
[129] "CMFISHCRT13_129" "CMFISHCRT13_130" "CMFISHCRT13_131" "CMFISHCRT13_132" "CMFISHCRT13_133" "CMFISHCRT13_134" "CMFISHCRT13_135" "CMFISHCRT13_136"
[137] "CMFISHCRT13_137" "CMFISHCRT13_138" "CMFISHCRT13_139" "CMFISHCRT13_140" "CMFISHCRT13_141" "CMFISHCRT13_142" "CMFISHCRT13_143" "CMFISHCRT13_144"
[145] "CMFISHCRT13_145" "CMFISHCRT13_146" "CMFISHCRT13_147" "CMFISHCRT13_148" "CMFISHCRT13_149" "CMFISHCRT13_150" "CMFISHCRT13_151" "CMFISHCRT13_152"
[153] "CMFISHCRT13_153" "CMFISHCRT13_154" "CMFISHCRT13_155" "CMFISHCRT13_156" "CMFISHCRT13_157" "CMFISHCRT13_158" "CMFISHCRT13_159" "CMFISHCRT13_160"
[161] "CMFISHCRT13_161" "CMFISHCRT13_162" "CMFISHCRT13_163" "CMFISHCRT13_164" "CMFISHCRT13_165" "CMFISHCRT13_166" "CMFISHCRT13_167" "CMFISHCRT13_168"
[169] "CMFISHCRT13_169" "CMFISHCRT13_170" "CMFISHCRT13_171" "CMFISHCRT13_172" "CMFISHCRT13_173" "CMFISHCRT13_174" "CMFISHCRT13_175" "CMFISHCRT13_176"
[177] "CMFISHCRT13_177" "CMFISHCRT13_178" "CMFISHCRT13_179" "CMFISHCRT13_180" "CMFISHCRT13_181" "CMFISHCRT13_182" "CMFISHCRT13_183" "CMFISHCRT13_184"
[185] "CMFISHCRT13_185" "CMFISHCRT13_186" "CMFISHCRT13_187" "CMFISHCRT13_188" "CMFISHCRT13_189" "CMFISHCRT13_190" "CMFISHCRT13_191" "CMFISHCRT13_192"
[193] "CMFISHCRT13_193" "CMFISHCRT13_194" "CMFISHCRT13_195" "CMFISHCRT13_196" "CMFISHCRT13_197" "CMFISHCRT13_198" "CMFISHCRT13_199" "CMFISHCRT13_200"
[201] "CMFISHCRT13_201" "CMFISHCRT13_202" "CMFISHCRT13_203" "CMFISHCRT13_204" "CMFISHCRT13_205" "CMFISHCRT13_206" "CMFISHCRT13_207" "CMFISHCRT13_208"
[209] "CMFISHCRT13_209" "CMFISHCRT13_210" "CMFISHCRT13_211" "CMFISHCRT13_212" "CMFISHCRT13_213" "CMFISHCRT13_214" "CMFISHCRT13_215" "CMFISHCRT13_216"
[217] "CMFISHCRT13_217" "CMFISHCRT13_218" "CMFISHCRT13_219" "CMFISHCRT13_220" "CMFISHCRT13_221" "CMFISHCRT13_222" "CMFISHCRT13_223" "CMFISHCRT13_224"
[225] "CMFISHCRT13_225" "CMFISHCRT13_226" "CMFISHCRT13_227" "CMFISHCRT13_228" "CMFISHCRT13_229" "CMFISHCRT13_230" "CMFISHCRT13_231" "CMFISHCRT13_232"
[233] "CMFISHCRT13_233" "CMFISHCRT13_234" "CMFISHCRT13_235" "CMFISHCRT13_236" "CMFISHCRT13_237" "CMFISHCRT13_238" "CMFISHCRT13_239" "CMFISHCRT13_240"
[241] "CMFISHCRT13_241"
Correct. Add these to metadata.
metadata <-
dplyr::bind_rows(metadata,
CMFISHCRT13.gcl %>%
dplyr::select(SILLY_CODE, FK_FISH_ID, SillySource, DNA_TRAY_CODE, DNA_TRAY_WELL_CODE, CAPTURE_DATE) %>%
dplyr::rename(silly = SILLY_CODE,
fish_id = FK_FISH_ID,
silly_source = SillySource,
dna_tray_code = DNA_TRAY_CODE,
dna_tray_well_code = DNA_TRAY_WELL_CODE,
date = CAPTURE_DATE) %>%
tidyr::unite(col = "sample", dna_tray_code:dna_tray_well_code, sep = "_", remove = FALSE) %>%
tidyr::unite(col = "silly_source", c(silly, fish_id), sep = "_", remove = FALSE) %>%
dplyr::mutate(franz_id = stringr::str_c(stringr::str_sub(silly, 1, 2), # 1st letter of species name, 1st letter of stream name
stringr::str_sub(silly, -2, -1), # 2 digit sample year
"_",
stringr::str_pad(fish_id, width = 5, pad = "0", side = "left")), # 5 digit fishid using padded zeros up front)
date = lubridate::as_date(date),
year = lubridate::year(date),
stream = "Fish - Douglas Island",
DOY = lubridate::yday(date) # day of year
)
)
Verify all samples have metadata.
table(geno_silly_source %in% metadata$silly_source)
TRUE
18810
Excellent, all present and accounted for.
Record which samples were genotyped.
metadata <- metadata %>%
dplyr::mutate(genotyped = silly_source %in% geno_silly_source)
How many samples per collection (silly)?
metadata %>%
dplyr::count(genotyped, silly) %>%
tidyr::pivot_wider(names_from = genotyped, values_from = n)
Check For Sample Duplicates
I got a weird many-to-many error in a join the first time I ran
through this script, so I want to check and see if there are actually
duplicate samples present and if so, remove them!
# bind all samples together into a single .gcl object
my.gcl <- sapply(sillyvec, function(silly) {
my.silly <- get(paste0(silly, ".gcl"))
}, simplify = FALSE) %>%
dplyr::bind_rows()
Any duplicate SillySource?
table(table(my.gcl$SillySource))
1
18810
Clean up
Are there metadata duplicates?
table(table(metadata$silly_source))
1 2
27197 45
Well son of a biscuit, there are!
metadata %>%
dplyr::filter(duplicated(silly_source) | duplicated(silly_source, fromLast = TRUE)) %>%
dplyr::arrange(silly_source)
Okay, they appear to be true duplicates, no conflicting columns.
Remove duplicate rows.
metadata <- metadata %>%
dplyr::distinct(silly_source, .keep_all = TRUE)
Filter QC Crosscheck Plates
Create a vector of plate IDs to toss, pending QC crosschecks.
plate_ids_qcxcheck <- c(64461, 64462, 64480, 64482, 64483, 64484, 64485, 64486, 64502, 64503, 64509, 64511, 64512, 64513, 64514, 64515, 64542, 64543, 64582, 56362, 56363) %>% as.character()
Get sample size by silly.
(
sample_size_qcxcheck <- dplyr::tibble(silly = sillyvec) %>%
dplyr::mutate(genotyped = GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Remove any fish on the plates pending QC crosschecks.
x <- sapply(sillyvec, function(silly) {
my.silly <- get(paste0(silly, ".gcl"))
my.silly <- my.silly %>% dplyr::filter(!stringr::str_detect(string = PLATE_ID, pattern = stringr::str_c(plate_ids_qcxcheck, collapse = "|")))
assign(x = paste0(silly, ".gcl"),
value = my.silly,
pos = 1)
})
rm(x) # suppresses output
How many fish were removed by silly?
(
sample_size_qcxcheck <- sample_size_qcxcheck %>%
dplyr::mutate(qcxcheck = genotyped - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
How many total samples?
sum(sample_size_qcxcheck$qcxcheck)
[1] 1995
Okay, that looks about right for 21 plates. Must have been some
partial plates.
Genotype QA
Standard data QA (GT-seq, no conScore):
- Remove fish missing genotypes (<80% loci)
- Remove contamination (heterozygosity outliers +/-
3.5 modified z-score)
- Remove duplicates (>95% loci concordance, remove
both duplicates)
(
sample_size_qa <- dplyr::tibble(silly = sillyvec) %>%
dplyr::mutate(genotyped = GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Missing Loci (<80%)
Remove fish with <80% loci genotyped.
miss_loci <-
GCLr::remove_ind_miss_loci(sillyvec = sillyvec,
proportion = 0.8)
G3;30 IDs were removed from CMADMCR13.gcl
gG3;30 IDs were removed from CMADMCR17.gcl
gG3;4 IDs were removed from CMADMCR18.gcl
gG3;6 IDs were removed from CMFISHCR13.gcl
gG3;110 IDs were removed from CMFISHCR14.gcl
gG3;111 IDs were removed from CMFISHCR17.gcl
gG3;79 IDs were removed from CMFISHCR18.gcl
gG3;182 IDs were removed from CMFISHCR19.gcl
gG3;6 IDs were removed from CMFISHCR21.gcl
gG3;29 IDs were removed from CMFISHCR22.gcl
gG3;36 IDs were removed from CMFISHCR23.gcl
gG3;2 IDs were removed from CMFISHCRT13.gcl
gG3;2 IDs were removed from CMFISHCRT21.gcl
gG3;3 IDs were removed from CMFISHCRT22.gcl
gG3;1 IDs were removed from CMFISHCRT23.gcl
gG3;42 IDs were removed from CMPROSCR13.gcl
gG3;5 IDs were removed from CMPROSCR14.gcl
gG3;17 IDs were removed from CMPROSCR17.gcl
gG3;8 IDs were removed from CMPROSCR18.gcl
gG3;13 IDs were removed from CMPROSCR19.gcl
gG3;2 IDs were removed from CMPROSCR21.gcl
gG3;9 IDs were removed from CMPROSCR22.gcl
gG3;2 IDs were removed from CMPROSCRT22.gcl
gG3;23 IDs were removed from CMSAWCR13.gcl
gG3;1 IDs were removed from CMSAWCR14.gcl
gG3;6 IDs were removed from CMSAWCR15.gcl
gG3;46 IDs were removed from CMSAWCR17.gcl
gG3;17 IDs were removed from CMSAWCR18.gcl
gG3;17 IDs were removed from CMSAWCR19.gcl
gG3;8 IDs were removed from CMSAWCR21.gcl
gG3;25 IDs were removed from CMSAWCR22.gcl
gG3;A total of 872 individuals were removed from sillys in sillyvec.
g
# miss_loci$IDs_Removed
GCLr::save_objects(objects = "miss_loci", path = "../objects", rds = TRUE)
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(missing = genotyped - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Overall 872 samples dropped due to poor quality DNA.
What was genotyping success by spawning state?
miss_loci_tib <- tibble::enframe(miss_loci$IDs_Removed, name = "silly", value = "fish_id") %>%
tidyr::unnest_longer(fish_id) %>%
dplyr::left_join(y = metadata, by = dplyr::join_by(silly, fish_id))
metadata %>%
dplyr::filter(genotyped) %>%
dplyr::mutate(drop_miss = silly_source %in% miss_loci_tib$silly_source) %>%
dplyr::count(drop_miss, spawning_state) %>%
tidyr::pivot_wider(names_from = drop_miss, values_from = n) %>%
dplyr::rename(n_drop = `TRUE`, n_keep = `FALSE`) %>%
dplyr::mutate(n = n_keep + n_drop,
p_keep = n_keep / n,
spawning_state = factor(spawning_state, levels = c("Alive", "Pink Gill", "Grey Gill", "Rotting"))) %>%
dplyr::arrange(spawning_state)
As expected, genotyping success was much lower for rotting fish.
Contamination (Heterozygosity > +/- 3.5 modified Z-score)
Remove contaminated samples prior to checking for duplicates!
Individual heterozygosity outliers is our best metric for removing
contaminated samples in the absence of something more sophisticated like
conScore from GTscore.
In previous analyses, including Shedd et al. 2022, we’ve
used the standard 1.5 IQR outlier detection method to remove excessively
heterozygous individuals. However, the 1.5 IQR method assumes a normal
distribution, but heart sample heterozygosities tend to be skewed right
due to contamination. As of 2024-02-08, we decided to pivot to using modified
Z-scores with cutoffs of +/- 3.5 as recommended by Iglewicz and
Hoaglin . The modified Z-score uses the median and
median absolute deviation (MAD) instead of the mean and standard
deviation, and is thus more robust to outliers and asymmetrical
distributions.
Source function and calculate individual heterozygosities from
AHRP-PWS-Pink-Salmon-Pedigrees GitHub repo.
source("~/GitHub_repos/AHRP-PWS-Pink-Salmon-Pedigrees/code/functions/calc_ind_het.R")
(
ind_het <-
calc_ind_het(
sillyvec = sillyvec,
loci = loci251,
ncores = parallel::detectCores() - 4
)
)
Time difference of 17.24236 secs
Calculate +/- 3.5 modified z-score cutoffs by stream.
NOTE this means that the exact heterozygosity cutoff
will vary slightly by stream. For PWS Pink Salmon, I did it separately
by silly, but some of our sample sizes are pretty small here and we have
the separate “tag” sillys. I’m opting to do this by stream to allow for
subtle changes in allele frequency per stream.
# Function to calculate median absolute deviation (MAD)
mad <- function(x) {
median(abs(x - median(x)))
}
# Calculate the modified z-score for each individual
(
ind_het <- ind_het %>%
dplyr::mutate(
year = 2000 + as.numeric(stringr::str_sub(
string = silly,
start = -2,
end = -1
)),
stream = dplyr::case_when(
stringr::str_detect(string = silly, pattern = "ADM") ~ "Admiralty",
stringr::str_detect(string = silly, pattern = "FISH") ~ "Fish",
stringr::str_detect(string = silly, pattern = "PROS") ~ "Prospect",
stringr::str_detect(string = silly, pattern = "SAW") ~ "Sawmill",
TRUE ~ "mistakes_were_made"
)
) %>%
dplyr::group_by(stream) %>% # cutoffs are specific to each stream!
dplyr::mutate(
modified_z_score = 0.6745 * (het - median(het)) / mad(het),
outlier = dplyr::case_when(abs(modified_z_score) > 3.5 ~ TRUE, TRUE ~ FALSE)
) %>%
dplyr::ungroup()
)
GCLr::save_objects("ind_het", path = "objects", rds = TRUE)
Plot distribution of heterozygosity and show outliers.
ind_het %>%
ggplot2::ggplot(ggplot2::aes(x = het, fill = outlier)) +
ggplot2::geom_histogram(binwidth = 1 / length(loci251)) +
ggplot2::facet_grid(rows = ggplot2::vars(stream), scales = "free_y") +
ggplot2::xlim(0, 1) +
ggplot2::scale_fill_manual(name = "Outlier", values = c("TRUE" = "black", "FALSE" = "grey60")) +
ggplot2::xlab("Individual Heterozygosity") +
ggplot2::ylab("Frequency") +
ggplot2::ggtitle("Individual Heterozygosity - By Stream") +
ggplot2::theme_bw(base_size = 14)

Remove outliers.
output <- lapply(sillyvec, function(x) {
GCLr::remove_ids(
silly = x,
IDs = ind_het %>% dplyr::filter(outlier, silly == x) %>% dplyr::pull(fish_id)
)
})
G3;8 IDs were removed from CMADMCR13.gcl
gG3;5 IDs were removed from CMADMCR14.gcl
gG3;147 IDs were removed from CMADMCR17.gcl
gG3;2 IDs were removed from CMADMCR18.gcl
gG3;11 IDs were removed from CMFISHCR13.gcl
gG3;113 IDs were removed from CMFISHCR14.gcl
gG3;111 IDs were removed from CMFISHCR17.gcl
gG3;49 IDs were removed from CMFISHCR18.gcl
gG3;55 IDs were removed from CMFISHCR19.gcl
gG3;2 IDs were removed from CMFISHCR20.gcl
gG3;7 IDs were removed from CMFISHCR21.gcl
gG3;17 IDs were removed from CMFISHCR22.gcl
gG3;100 IDs were removed from CMFISHCR23.gcl
gG3;1 IDs were removed from CMFISHCRT13.gcl
gG3;0 IDs were removed from CMFISHCRT21.gcl
gG3;1 IDs were removed from CMFISHCRT22.gcl
gG3;3 IDs were removed from CMFISHCRT23.gcl
gG3;18 IDs were removed from CMPROSCR13.gcl
gG3;10 IDs were removed from CMPROSCR14.gcl
gG3;153 IDs were removed from CMPROSCR17.gcl
gG3;6 IDs were removed from CMPROSCR18.gcl
gG3;50 IDs were removed from CMPROSCR19.gcl
gG3;0 IDs were removed from CMPROSCR20.gcl
gG3;2 IDs were removed from CMPROSCR21.gcl
gG3;10 IDs were removed from CMPROSCR22.gcl
gG3;0 IDs were removed from CMPROSCRT21.gcl
gG3;0 IDs were removed from CMPROSCRT22.gcl
gG3;17 IDs were removed from CMSAWCR13.gcl
gG3;2 IDs were removed from CMSAWCR14.gcl
gG3;4 IDs were removed from CMSAWCR15.gcl
gG3;93 IDs were removed from CMSAWCR17.gcl
gG3;1 IDs were removed from CMSAWCR18.gcl
gG3;0 IDs were removed from CMSAWCR19.gcl
gG3;0 IDs were removed from CMSAWCR20.gcl
gG3;2 IDs were removed from CMSAWCR21.gcl
gG3;0 IDs were removed from CMSAWCR22.gcl
gG3;0 IDs were removed from CMSAWCRT21.gcl
gG3;0 IDs were removed from CMSAWCRT22.gcl
g
message(paste0("A total of ", ind_het %>% dplyr::filter(outlier) %>% nrow(), " individuals were removed from sillys in sillyvec."))
G3;A total of 1000 individuals were removed from sillys in sillyvec.
g
Not too many contaminated samples! Most likely culled during missing
loci.
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(heterozygosity = genotyped - missing - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Overall 1,000 samples dropped due to sample contamination.
Duplicate (>=95%)
Identify potential duplicate genotypes (>=95% loci).
duplicate_check_95 <-
GCLr::dupcheck_within_silly(
sillyvec = sillyvec,
minproportion = 0.95,
minnonmissing = 0.6,
ncores = parallel::detectCores() - 4
)
Time difference of 21.62875 secs
GCLr::save_objects("duplicate_check_95", path = "objects", rds = TRUE)
How many duplicate pairs by silly?
duplicate_check_95 %>%
dplyr::count(silly) %>%
dplyr::arrange(dplyr::desc(n))
Remove both duplicates! As opposed to GSI work,
where we want to keep individuals but aren’t typically worried about
paired data, here we want to remove both individuals as the paired data
integrity (including otolith reads!) is lost.
duplicates_removed <-
GCLr::remove_dups(dupcheck = duplicate_check_95, remove_both = TRUE)
G3;2 IDs were removed from CMADMCR14.gcl
gG3;20 IDs were removed from CMADMCR17.gcl
gG3;6 IDs were removed from CMADMCR18.gcl
gG3;2 IDs were removed from CMFISHCR13.gcl
gG3;12 IDs were removed from CMFISHCR14.gcl
gG3;14 IDs were removed from CMFISHCR17.gcl
gG3;38 IDs were removed from CMFISHCR18.gcl
gG3;18 IDs were removed from CMFISHCR19.gcl
gG3;4 IDs were removed from CMFISHCR23.gcl
gG3;2 IDs were removed from CMFISHCRT23.gcl
gG3;2 IDs were removed from CMPROSCR13.gcl
gG3;4 IDs were removed from CMPROSCR14.gcl
gG3;14 IDs were removed from CMPROSCR17.gcl
gG3;4 IDs were removed from CMPROSCR18.gcl
gG3;10 IDs were removed from CMPROSCR19.gcl
gG3;4 IDs were removed from CMPROSCR20.gcl
gG3;2 IDs were removed from CMPROSCR22.gcl
gG3;8 IDs were removed from CMSAWCR17.gcl
gG3;4 IDs were removed from CMSAWCR18.gcl
gG3;2 IDs were removed from CMSAWCR22.gcl
g
GCLr::save_objects("duplicates_removed", path = "objects", rds = TRUE)
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(
duplicate = genotyped - missing - heterozygosity - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n)
)
)
Overall 157 samples dropped due to duplicate genotypes.
Final
Final, post-QA sample sizes by silly.
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(
final = GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n)
)
)
GCLr::save_objects("sample_size_qa", path = "objects", rds = TRUE)
fs::dir_create("output")
readr::write_csv(x = sample_size_qa, file = "output/sample_size_qa.csv")
Save
Save final genotypes and update metadata.
Genotypes
Save post-QA genotypes with joined metadata.
fs::dir_create("data/genotypes/postQA")
GCLr::save_sillys(sillyvec = sillyvec, path = "data/genotypes/postQA", rds = TRUE)
QA Conclusions
Overall 18,810 total pedigree samples were genotyped; 1,995 were
removed due to pending QC Crosschecks; 872 were removed due to poor
quality DNA (missing >= 20% loci); 1,000 were removed due to
contaminated DNA (heterozygosity >= 3.5 modified z-score); and 157
were removed due to duplicate genotypes (>= 95% loci), leaving a
total of 14,786 in the final dataset.
Adaptive Loci Genotypes
Create a single object with just the metadata and adaptive loci
genotypes.
Calculate Allele Frequencies
Calculate the allele counts for each locus by stream, year, and DOY.
Adapted from Pat Barry’s code in AHRP_ChumAdaptive.html
loci_adaptive <- grep(pattern = "OkeV2", x = loci251, value = TRUE)
# testing
# chum_adaptive_geno %>%
# dplyr::select(silly_source, stream, year, DOY, tidyselect::starts_with(loci_adaptive[1])) %>%
# tidyr::pivot_longer(cols = tidyselect::starts_with(loci_adaptive[1]), names_to = "locus", values_to = "allele") %>%
# dplyr::mutate(allele = factor(allele),
# allele = as.numeric(allele)) %>% # make factor and convert to numeric
# dplyr::count(stream, year, DOY, allele) %>%
# dplyr::mutate(locus = loci_adaptive[1])
# calculate the number of each allele observed per stream, year, day
(chum_adaptive_allele_counts <- lapply(loci_adaptive, function(locus) {
chum_adaptive_geno %>%
dplyr::select(silly_source,
stream,
year,
DOY,
tidyselect::starts_with(locus)) %>%
tidyr::pivot_longer(
cols = tidyselect::starts_with(locus),
names_to = "locus",
values_to = "allele"
) %>%
dplyr::mutate(allele = factor(allele),
allele = as.numeric(allele)) %>% # make factor and convert to numeric
dplyr::count(stream, year, DOY, allele) %>%
dplyr::mutate(locus = locus)
}) %>%
dplyr::bind_rows())
Convert to alelle frequencies.
(
chum_adaptive_allele_freq <- chum_adaptive_allele_counts %>%
tidyr::pivot_wider(names_from = allele, values_from = n) %>%
dplyr::select(-`NA`) %>% # drop no-calls
dplyr::mutate(
`1` = replace_na(`1`, 0),
# account for unobserved alleles on a given day
`2` = replace_na(`2`, 0)
) %>%
tidyr::pivot_longer(
cols = `1`:`2`,
names_to = "allele",
values_to = "n"
) %>%
dplyr::group_by(stream, year, DOY, locus) %>%
dplyr::mutate(
n_alleles = sum(n),
p_alleles = n / n_alleles,
SNP = paste(locus, allele, sep = "_")
) %>%
dplyr::ungroup()
)
View Allele Frequencies
# TODO
# double check allele frequency code to make sure it is correct
# make sure alleles are being assigned 1 vs. 2 consistently
# filter out hatchery strays?
# check sample sizes by stream/year/DOY?
# see if spawning state correlates?
Admiralty
chum_adaptive_allele_freq %>%
dplyr::filter(stream == "Admiralty") %>%
dplyr::mutate(locus = stringr::str_remove_all(string = locus, pattern = "OkeV2_")) %>%
ggplot2::ggplot(ggplot2::aes(x = DOY, y = p_alleles, colour = SNP, group_by(SNP))) +
ggplot2::geom_point() +
ggplot2::geom_smooth(method = "lm", weight = "n_alleles", formula = y~x) +
ggplot2::facet_grid(locus ~ year) +
ggplot2::theme_bw() +
ggplot2::theme(legend.position = 'none')

Fish
chum_adaptive_allele_freq %>%
dplyr::filter(stream == "Fish - Douglas Island") %>%
dplyr::mutate(locus = stringr::str_remove_all(string = locus, pattern = "OkeV2_")) %>%
ggplot2::ggplot(ggplot2::aes(x = DOY, y = p_alleles, colour = SNP, group_by(SNP))) +
ggplot2::geom_point() +
ggplot2::geom_smooth(method = "lm", weight = "n_alleles", formula = y~x) +
ggplot2::facet_grid(locus ~ year) +
ggplot2::theme_bw() +
ggplot2::theme(legend.position = 'none')

Prospect
chum_adaptive_allele_freq %>%
dplyr::filter(stream == "Prospect") %>%
dplyr::mutate(locus = stringr::str_remove_all(string = locus, pattern = "OkeV2_")) %>%
ggplot2::ggplot(ggplot2::aes(x = DOY, y = p_alleles, colour = SNP, group_by(SNP))) +
ggplot2::geom_point() +
ggplot2::geom_smooth(method = "lm", weight = "n_alleles", formula = y~x) +
ggplot2::facet_grid(locus ~ year) +
ggplot2::theme_bw() +
ggplot2::theme(legend.position = 'none')

Sawmill
chum_adaptive_allele_freq %>%
dplyr::filter(stream == "Sawmill") %>%
dplyr::mutate(locus = stringr::str_remove_all(string = locus, pattern = "OkeV2_")) %>%
ggplot2::ggplot(ggplot2::aes(x = DOY, y = p_alleles, colour = SNP, group_by(SNP))) +
ggplot2::geom_point() +
ggplot2::geom_smooth(method = "lm", weight = "n_alleles", formula = y~x) +
ggplot2::facet_grid(locus ~ year) +
ggplot2::theme_bw() +
ggplot2::theme(legend.position = 'none')

end
LS0tDQp0aXRsZTogIkV4cG9ydCBHZW5vdHlwZXMgZm9yIDkgQWRhcHRpdmUgUnVuIFRpbWluZyBMb2NpIHRvIE5PQUEiDQpzdWJ0aXRsZTogIkZpbHRlcmVkIGZvciBQbGF0ZXMgTm90IE5lZWRpbmcgUUMgQ3Jvc3NjaGVja3MiDQphdXRob3I6ICJLeWxlIFNoZWRkIg0KZGF0ZTogIlN0YXJ0ZWQ6IDIwMjUtMDctMjQsIGxhc3Qgb3BlbmVkOiBgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogdW5pdGVkDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHRydWUNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCiMgU2V0dXANCg0KYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0Kcm0obGlzdCA9IGxzKGFsbC5uYW1lcyA9IFRSVUUpKQ0KDQppZighcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpOyBsaWJyYXJ5KHBhY21hbikNCg0KcGFjbWFuOjpwX2xvYWQoDQogIHRpZHl2ZXJzZSwNCiAgc2NhbGVzLA0KICBqYW5pdG9yLA0KICBHQ0xyLA0KICBjb2luDQopDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSAxMCkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCg0KLnVzZXJuYW1lID0gcmVhZExpbmVzKCJ+L3Vzcl9wdy50eHQiLCBuID0gMSkgICMgTE9LSSB1c2VybmFtZQ0KLnBhc3N3b3JkID0gcmVhZExpbmVzKCJ+L3Vzcl9wdy50eHQiICwgbiA9IDIpW1syXV0gICMgTE9LSSBwYXNzd29yZA0KYGBgDQoNCiMgT2JqZWN0aXZlDQoNClRoZSBwdXJwb3NlIG9mIHRoaXMgbm90ZWJvb2sgaXMgdG8gcHJvdmlkZSBXZXMgTGFyc29uIChOT0FBIEFCTCkgd2l0aCBhZGFwdGl2ZSBydW4gdGltaW5nIGdlbm90eXBlcyAoOSBsb2NpKSBmb3IgQUhSUCBjaHVtIHNhbXBsZXMuIEZpbHRlciBvdXQgcGxhdGVzIHVuZGVyZ29pbmcgUUMgY3Jvc3NjaGVja3MgZHVlIHRvIHBvb3IgZ2Vub3R5cGluZyBzdWNjZXNzLiBXZXMgaXMgaW50ZXJlc3RlZCBpbiByZXByZXNlbnRhdGlvbiBhY3Jvc3MgeWVhcnMsIGxlc3MgYWNyb3NzIG11bHRpcGxlIHN0cmVhbXMuIEhlIHdhbnRzIHRvIHNlZSBob3cgdGhlIHBhdHRlcm5zIGhvbGQgdXAgb3ZlciB0aW1lLg0KDQojIyBTdGVwcw0KDQogICogaW1wb3J0IGFsbCBleGlzdGluZyBnZW5vdHlwZXMgZm9yIENNMDY0LUNNMDcwDQogICogZmlsdGVyIG91dCB1bnRydXN0d29ydGh5IHBsYXRlcyB0aGF0IG5lZWQgUUMgY3Jvc3MgY2hlY2tzDQogICogc3RhbmRhcmQgR1Qtc2VxIGdlbm90eXBlIFFBIChtaXNzaW5nLCBoZXRlcm96eWdvc2l0eSwgZHVwbGljYXRlKQ0KICAqIGpvaW4gc2FtcGxlIGRhdGUgYW5kIHNwYXduaW5nIHN0YXRlIDwiLi4vT2NlYW5BSy9BSFJQIFNhbG1vbiBCaW9sb2dpY2FsIERhdGEgMjAyNDAxMDVfMTIyNDA0LjM2ODQ1OV93aXRoX3NwYXduaW5nX3N0YXRlLmNzdiI+DQogICogZmlsdGVyIGZvciA5IGFkYXB0aXZlIHJ1biB0aW1pbmcgbG9jaQ0KICAqIGV4cG9ydCBnZW5vdHlwZXMgd2l0aCBkYXRlL3NwYXduaW5nIHN0YXRlDQoNCiMgQmFja2dyb3VuZA0KDQpBbiBhZGFwdGl2ZSBtYXJrZXIgcGFuZWwgYXNzb2NpYXRlZCB3aXRoIHJ1biB0aW1pbmcgdmFyaWF0aW9uIGluIGNodW0gc2FsbW9uIHdhcyBkZXZlbG9wZWQgYnkgTk9BQSBBQkwgYW5kIHVzZWQgdG8gZ2Vub3R5cGUgdHdvIHBsYXRlcyBvZiBzYW1wbGVzIGZyb20gdGhlIEFERkcgQWxhc2thIEhhdGNoZXJ5IFJlc2VhcmNoIFByb2dyYW0gKEFIUlApIHN0dWR5IGJlaW5nIGNvbmR1Y3RlZCB3aXRoaW4gU0UgQWxhc2thLiBBSFJQIHNhbXBsZXMgKEZpc2ggQ3JlZWsgLSBEb3VnbGFzIElzbGFuZCAyMDIzICsgUHJvc3BlY3QgQ3JlZWsgMjAxNy8yMDE4KSB3ZXJlIGdlbm90eXBlZCBpbiBBdWd1c3QgMjAyNCBmb3IgMjAgc2luZ2xlIG51Y2xlb3RpZGUgcG9seW1vcnBoaXNtcyBhbmQgd2VpZ2h0ZWQgbGluZWFyIHJlZ3Jlc3Npb25zIHdlcmUgcGVyZm9ybWVkIHRvIGFzc2VzcyB0aGUgYXNzb2NpYXRpb24gb2YgZGlmZmVyZW50IFNOUHMgd2l0aCBydW4gdGltaW5nIChkYXRlIG9mIGNvbGxlY3Rpb24pLiBUaGUgY2h1bSBzYWxtb24gYWRhcHRpdmUgbWFya2VyIHBhbmVsIGNvbnRhaW5zIDIwIGxvY2kgc3ByZWFkIGFtb25nIDUgY2hyb21vc29tZXMgYW5kIHdhcyBkZXZlbG9wZWQgZnJvbSB2YXJpYXRpb24gZGV0ZWN0ZWQgYmV0d2VlbiBZdWtvbiBSaXZlciBzdW1tZXIgKEdpc2FzYSBSaXZlcikgYW5kIGZhbGwgKFBlbGx5IFJpdmVyKSBydW4gY2h1bSBzYWxtb24gd2l0aCBsb3cgY292ZXJhZ2Ugd2hvbGUgZ2Vub21lIHNlcXVlbmNpbmcuVGhlc2UgcG9wdWxhdGlvbnMgc3Bhd24gYXBwcm94aW1hdGVseSB0d28gbW9udGhzIGFuZCBvdmVyIDE1MDAgcml2ZXIga20gYXBhcnQuIE5PQUEgdGFyZ2V0ZWQgdGhlIG1ham9yIHBlYWtzIGluIGRpZmZlcmVudGlhdGlvbiB3aXRoIHBhcnRpY3VsYXIgaW50ZXJlc3QgaW4gbWFya2VycyB3aXRoaW4gdGhlIGdlbmVzIGxldWNpbmUgcmljaCByZXBlYXQgY29udGFpbmluZyBuaW5lIChMUlJDOTsgTEczNSkgYW5kIGVzdHJvZ2VuIHJlY2VwdG9yIM6yIChFU1JCOyBMRzI5KSBhcyB0aGV5IHdlcmUgYXNzb2NpYXRlZCB3aXRoIHJldHVybiB0aW1pbmcgaW4gb3RoZXIgc2FsbW9uaWQgc3BlY2llcy4gRWlnaHQgU05QcyAoZm91ciBvbiBMRzI5IGFuZCBmb3VyIG9uIExHMzUpIGFwcGVhciB0byBiZSBhc3NvY2lhdGVkIHdpdGggcnVuIHRpbWluZyB3aXRoaW4gdGhlc2UgY29sbGVjdGlvbnMuIFRoZSBzdHJlbmd0aCBvZiBhc3NvY2lhdGlvbiBmb3IgQUhSUCBzYW1wbGVzIGFwcGVhcnMgdG8gdmFyeSBieSBwb3B1bGF0aW9uLiBObyBtYXJrZXJzIG9uIExHcyA1LCAyMyBvciAxNSBhcHBlYXIgdG8gaGF2ZSBhIHN0cm9uZyBhc3NvY2lhdGlvbiB3aXRoIHJ1biB0aW1pbmcgd2l0aGluIHRoZXNlIGNvbGxlY3Rpb25zLiBPdmVyYWxsLCB0aGUgcmVzdWx0cyBmcm9tIExHMjkgYW5kIExHMzUgYXJlIHByb21pc2luZyBhbmQgdmFyaWF0aW9uIGluIHRoZXNlIFNOUHMgc2hvdWxkIGJlIHN1cnZleWVkIGZvciBhIGxhcmdlciBjb2xsZWN0aW9uIG9mIHNhbXBsZXMuDQoNCkEgdG90YWwgb2YgOSBhZGFwdGl2ZSwgcnVuIHRpbWluZyBsb2NpIHdlcmUgYWRkZWQgdG8gdGhlIEFIUlAgU0VBSyBDaHVtIHBhcmVudGFnZSBwYW5lbDogDQoNCiAgKiBPa2VWMl9MRzIzXzEwNTQ2MjM3DQogICogT2tlVjJfTEcyOV8yNTQ1MDc1Mg0KICAqIE9rZVYyX0xHMjlfMjU0NTU4MzMNCiAgKiBPa2VWMl9MRzI5XzI1NDgzNTk1DQogICogT2tlVjJfTEcyOV8yNTUxNTE2MQ0KICAqIE9rZVYyX0xHMzVfMjgwNzg4NjcNCiAgKiBPa2VWMl9MRzM1XzI4MTI4Njg3DQogICogT2tlVjJfTEczNV8yODE2NTA3Ng0KICAqIE9rZVYyX0xHMzVfMjgxNjcxNzINCg0KQWxsIEFIUlAgQ2h1bSBzYW1wbGVzIGZyb20gRmlzaCBDcmVlayAtIERvdWdsYXMgSXNsYW5kLCBQcm9zcGVjdCBDcmVlaywgQWRtaXJhbHR5IENyZWVrLCBhbmQgU2F3bWlsbCBDcmVlayB3ZXJlIGdlbm90eXBlZCBmb3IgdGhlIHBhcmVudGFnZSBwYW5lbCBhdCB0aGUgQURGJkcgR2VuZSBDb25zZXJ2YXRpb24gTGFib3JhdG9yeSAoR0NMKSBpbiBXaW50ZXIvU3ByaW5nIDIwMjUuIER1ZSB0byBwb29yIHRpc3N1ZSBxdWFsaXR5LCB0aGVyZSB3YXMgYSBoaWdoIHByb3BvcnRpb24gb2Ygc2FtcGxlcyB0aGF0IGZhaWxlZCB0byBnZW5vdHlwZSBhbmQvb3IgYXBwZWFyIHRvIGJlIGNvbnRhbWluYXRlZCAoaGlnaCBoZXRlcm96eWdvc2l0eSksIHJlc3VsdGluZyBpbiBsb3cgUUMgcG93ZXIgZm9yIHNldmVyYWwgcGxhdGVzLiBRQyBjcm9zc2NoZWNrcyBhcmUgY3VycmVudGx5IHVuZGVyd2F5IGF0IHRoZSBHQ0wgaW4gSnVseSAyMDI1LCBob3dldmVyLCBXZXMgd2FudHMgd2hhdGV2ZXIgZ2Vub3R5cGVzIHdlIGhhdmUgb24gaGFuZCBub3cgc28gd2lsbCBqdXN0IGZpbHRlciBvdXQgdGhvc2UgcGxhdGVzLg0KDQojIEltcG9ydCBEYXRhDQoNCiMjICpMb2N1c0NvbnRyb2wqDQoNCkNyZWF0ZSBgb2JqZWN0c2AsIGNyZWF0ZSAqTG9jdXNDb250cm9sKiBmb3IgYENodW1fQUhSUF8yNTdfdjEuMy4wLmANCmBgYHtyIExvY3VzQ29udHJvbCwgZXZhbD1GQUxTRX0NCmZzOjpkaXJfY3JlYXRlKCJvYmplY3RzIikNCkdDTHI6OmNyZWF0ZV9sb2N1c2NvbnRyb2wobWFya2Vyc3VpdGUgPSAiQ2h1bV9BSFJQXzI1N192MS4zLjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VybmFtZSA9IC51c2VybmFtZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzc3dvcmQgPSAucGFzc3dvcmQpDQpHQ0xyOjpzYXZlX29iamVjdHMob2JqZWN0cyA9ICJMb2N1c0NvbnRyb2wiLCBwYXRoID0gIm9iamVjdHMiKQ0KYGBgDQoNCkhybW0sIG9ubHkgMjUxIGxvY2kuIENvbmZpcm0gdGhhdCB0aGVzZSBhcmUgdGhlIHNhbWUgbG9jaSBydW4gaW4gdGhlIG1vc3QgcmVjZW50IEFIUlAgQ2h1bSBwcm9qZWN0IENNMDY5DQpgYGB7ciwgZXZhbD1GQUxTRX0NCnByb2JlX2xvY2lfQ00wNjkgPC0gcmVhZHI6OnJlYWRfdHN2KA0KICBmaWxlID0gIlY6L0xhYi9HZW5vdHlwaW5nL1NOUCBQcm9qZWN0cy9DaHVtL1Byb2plY3QgQ00wNjkgQUhSUCBQYXJlbnRhZ2UvR2Vub3R5cGUgRGF0YSBGaWxlcy9zZXQycnJfQ00wNjlfb3V0cHV0cy9wcm9iZXMudHh0IiwgDQogIHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JQ0KICBkcGx5cjo6ZGlzdGluY3QoYCNMb2N1c19JRGApICU+JQ0KICBkcGx5cjo6cHVsbCgpDQoNCmFsbC5lcXVhbChwcm9iZV9sb2NpX0NNMDY5LCBMb2N1c0NvbnRyb2wkbG9jdXNuYW1lcykNCmBgYA0KDQpZZXMsIGFsbCBhcmUgdGhlIHNhbWUuDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmxvY2kyNTEgPC0gTG9jdXNDb250cm9sJGxvY3VzbmFtZXMNCkdDTHI6OnNhdmVfb2JqZWN0cyhvYmplY3RzID0gImxvY2kyNTEiLCBwYXRoID0gIm9iamVjdHMiKQ0KYGBgDQoNCiMjIEdlbm90eXBlcw0KDQpSZWFkIGluIGdlbm90eXBlcyBieSBwcm9qZWMgdG8gZ2V0IHNpbGx5dmVjLg0KYGBge3IsIGV2YWw9RkFMU0V9DQpybShMb2N1c0NvbnRyb2wpDQojIEdDTHI6Omxva2kycl9wcm9qKHByb2plY3RfbmFtZSA9IHBhc3RlMCgiQ00wIiwgNjQ6NzApLCB1c2VybmFtZSA9IC51c2VybmFtZSwgcGFzc3dvcmQgPSAucGFzc3dvcmQpICAjIGNvbW1lbnQgb3V0IHRvIGF2b2lkIHJlLXJ1bm5pbmcNCmBgYA0KDQpXaGF0IGNvbGxlY3Rpb25zPw0KYGBge3IsIGV2YWw9RkFMU0V9DQooc2lsbHl2ZWMgPC0gc29ydChwcm9qZWN0X3NpbGx5cykpDQpgYGANCg0KU3Vic2V0IGBzaWxseXZlY2AgdG8gcmVtb3ZlIGFsZXZpbiBjb2xsZWN0aW9uDQpgYGB7ciwgZXZhbD1GQUxTRX0NCihzaWxseXZlYyA8LSBncmVwKHBhdHRlcm4gPSAiYSIsIHggPSBzaWxseXZlYywgaWdub3JlLmNhc2UgPSBGQUxTRSwgdmFsdWUgPSBUUlVFLCBpbnZlcnQgPSBUUlVFKSkNCmBgYA0KDQpXaXBlIG91dCBgLmdjbGAgb2JqZWN0cyBhbmQgcmUtcmVhZCBpbiB1c2luZyBgbG9raTJyYC4NCmBgYHtyLCBldmFsPUZBTFNFfQ0KR0NMcjo6c2F2ZV9vYmplY3RzKG9iamVjdHMgPSAic2lsbHl2ZWMiLCBwYXRoID0gIm9iamVjdHMiKQ0KDQpybShsaXN0ID0gb2JqZWN0cyhwYXR0ZXJuID0gIi5nY2wiKSkNCnJtKExvY3VzQ29udHJvbCkNCmBgYA0KDQpSZWFkIGluIGdlbm90eXBlcy4NCmBgYHtyIGV2YWw9RkFMU0V9DQpHQ0xyOjpsb2FkX29iamVjdHMocGF0aCA9ICJvYmplY3RzIiwgcGF0dGVybiA9ICJMb2N1c0NvbnRyb2wiKQ0KDQpHQ0xyOjpsb2tpMnIoc2lsbHl2ZWMgPSBzaWxseXZlYywNCiAgICAgICAgICAgICB1c2VybmFtZSA9IC51c2VybmFtZSwNCiAgICAgICAgICAgICBwYXNzd29yZCA9IC5wYXNzd29yZCwgDQogICAgICAgICAgICAgdGVzdF90eXBlID0gIkdUU05QIikNCmBgYA0KDQpPbmx5IHRvb2sgNS4zOSBtaW51dGVzIHRvIHJlYWQgZXZlcnl0aGluZyBpbiBhdCB0aGUgb2ZmaWNlIQ0KDQpTYXZlIHJhdyBnZW5vdHlwZXMgKHVzZWZ1bCBmb3Igd29ya2luZyBvZmZzaXRlIGFuZCBvZmYgVlBOKS4NCmBgYHtyIGV2YWw9RkFMU0V9DQpmczo6ZGlyX2NyZWF0ZSgiZGF0YS9nZW5vdHlwZXMiKQ0KZnM6OmRpcl9jcmVhdGUoImRhdGEvZ2Vub3R5cGVzL3JhdyIpDQojIEdDTHI6OnNhdmVfc2lsbHlzKHNpbGx5dmVjID0gc2lsbHl2ZWMsIHBhdGggPSAiZGF0YS9nZW5vdHlwZXMvcmF3IiwgcmRzID0gVFJVRSkgICMgaGlkZSBzbyB5b3UgZG9uJ3Qgb3ZlcndyaXRlIG9uIGFjY2lkZW50IQ0KYGBgDQoNCg0KIyMjIFJlLWxvYWQNCg0KUmUtbG9hZCwgaWYgbmVjZXNzYXJ5DQpgYGB7cn0NCkdDTHI6OmxvYWRfb2JqZWN0cyhwYXRoID0gIm9iamVjdHMiKQ0KR0NMcjo6bG9hZF9zaWxseXMoc2lsbHl2ZWMgPSBzaWxseXZlYywgcGF0aCA9ICJkYXRhL2dlbm90eXBlcy9yYXciLCByZHMgPSBUUlVFKQ0KYGBgDQoNCiMjIE1ldGFkYXRhDQoNClJlYWQgaW4gcGFpcmVkIG1ldGFkYXRhIGZyb20gU2FsbW9uIEJpb2xvZ2ljYWwgRmFjdCBhbmQgU3RyZWFtIFNwZWNpbWVucywgZm9ybWF0IHNpbWlsYXIgdG8gYHB3c19waW5rX3BlZF9kYXRgLiAqKk5PVEUqKiBJJ20gZG9pbmcgdGhpcyBpbiBhIGJpdCBvZiBhIGh1cnJ5LCBzbyBtYWtlIHN1cmUgdG8gcHJvb2YgcmVhZCBjYXJlZnVsbHkgaWYgcmVjeWNsaW5nIHRoaXMgY29kZSENCmBgYHtyfQ0KKG1ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX2NzdihmaWxlID0gIi4uL09jZWFuQUsvQUhSUCBTYWxtb24gQmlvbG9naWNhbCBEYXRhIDIwMjQwMTA1XzEyMjQwNC4zNjg0NTlfd2l0aF9zcGF3bmluZ19zdGF0ZS5jc3YiKSkNCmBgYA0KDQpNb2RpZnkNCmBgYHtyfQ0KKA0KICBtZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgDQogICAgZHBseXI6OnNlbGVjdCgtZHBseXI6OmNvbnRhaW5zKCJ0YXJnZXQiKSkgJT4lIA0KICAgIGRwbHlyOjpzZWxlY3QoLWRwbHlyOjpjb250YWlucygiZGV0ZXJtaW5hdGlvbiIpKSAlPiUgDQogICAgZHBseXI6OnJlbmFtZShkYXRlID0gc2FtcGxlX2RhdGUsDQogICAgICAgICAgICAgICAgICBsZW5ndGggPSBsZW5ndGhfbW0sDQogICAgICAgICAgICAgICAgICBzaWxseSA9IHNpbGx5X2NvZGUpICU+JSANCiAgICB0aWR5cjo6dW5pdGUoY29sID0gInNhbXBsZSIsIGRuYV90cmF5X2NvZGU6ZG5hX3RyYXlfd2VsbF9jb2RlLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgDQogICAgdGlkeXI6OnVuaXRlKGNvbCA9ICJzaWxseV9zb3VyY2UiLCBjKHNpbGx5LCBmaXNoX2lkKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUoZnJhbnpfaWQgPSBzdHJpbmdyOjpzdHJfYyhzdHJpbmdyOjpzdHJfc3ViKHNpbGx5LCAxLCAyKSwgICMgMXN0IGxldHRlciBvZiBzcGVjaWVzIG5hbWUsIDFzdCBsZXR0ZXIgb2Ygc3RyZWFtIG5hbWUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3N1YihzaWxseSwgLTIsIC0xKSwgICMgMiBkaWdpdCBzYW1wbGUgeWVhcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl9wYWQoZmlzaF9pZCwgd2lkdGggPSA1LCBwYWQgPSAiMCIsIHNpZGUgPSAibGVmdCIpKSwgICMgNSBkaWdpdCBmaXNoaWQgdXNpbmcgcGFkZGVkIHplcm9zIHVwIGZyb250KQ0KICAgICAgICAgICAgICAgICAgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihkYXRlKSwNCiAgICAgICAgICAgICAgICAgIHN0cmVhbSA9IHN0cmluZ3I6OnN0cl9yZW1vdmUoc3RyaW5nID0gbG9jYXRpb25fY29kZSwgcGF0dGVybiA9ICIgQ3JlZWsiKSwNCiAgICAgICAgICAgICAgICAgIERPWSA9IGx1YnJpZGF0ZTo6eWRheShkYXRlKSwgICMgZGF5IG9mIHllYXINCiAgICAgICAgICAgICAgICAgIHNleCA9IGRwbHlyOjpjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgICAgIHNleCA9PSAiTSIgfiAiTWFsZSIsDQogICAgICAgICAgICAgICAgICAgIHNleCA9PSAiRiIgfiAiRmVtYWxlIiwNCiAgICAgICAgICAgICAgICAgICAgc2V4ID09ICJVIiB+IE5BX2NoYXJhY3Rlcl8sDQogICAgICAgICAgICAgICAgICAgIGlzLm5hKHNleCkgfiBOQV9jaGFyYWN0ZXJfKSwNCiAgICAgICAgICAgICAgICAgIG9yaWdpbiA9IGRwbHlyOjpjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgICAgIG90b2xpdGhfbWFya19wcmVzZW50ID09ICJOTyIgfiAiTmF0dXJhbCIsDQogICAgICAgICAgICAgICAgICAgIG90b2xpdGhfbWFya19wcmVzZW50ID09ICJZRVMiIH4gIkhhdGNoZXJ5Ig0KICAgICAgICAgICAgICAgICAgKSwgICMgYWRkIG9yaWdpbiB2YXJpYWJsZQ0KICAgICAgICAgICAgICAgICAgb3JpZ2luID0gYmFzZTo6ZmFjdG9yKG9yaWdpbiwgbGV2ZWxzID0gYygiTmF0dXJhbCIsICJIYXRjaGVyeSIpKSwgICMgbWFrZSBmYWN0b3IgdG8gZW5zdXJlIGhhdGNoZXJ5ICE9IHJlZA0KICAgICAgICAgICAgICAgICAgc3Bhd25pbmdfc3RhdGUgPSBkcGx5cjo6Y2FzZV93aGVuKA0KICAgICAgICAgICAgICAgICAgICBzcGF3bmluZ19zdGF0ZSA9PSAiVW5rbm93biIgfiBOQV9jaGFyYWN0ZXJfLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gc3Bhd25pbmdfc3RhdGUNCiAgICAgICAgICAgICAgICAgICkgICMgZm9yY2UgVW5rbm93biB0byBOQQ0KICAgICkgJT4lICANCiAgICBkcGx5cjo6c2VsZWN0KA0KICAgICAgZnJhbnpfaWQsICAjIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBGUkFOeiAoZS5nLiBQRTE2XzAwMDAxLCBzcGVjaWVzLCBzdHJlYW0sIHllYXIsIEdDTCBmaXNoX2lkKQ0KICAgICAgc2FtcGxlLCAgIyB1bHRpbWF0ZSBkYXRhIGtleSBmb3IgQUhSUCwgRFdQIGJhcmNvZGUgKyBEV1AgcG9zaXRpb24NCiAgICAgIHNpbGx5X3NvdXJjZSwgICMgdW5pcXVlIGlkZW50aWZpZXIgZm9yIExPS0kgKEdDTCBkYXRhYmFzZSkNCiAgICAgIHN0cmVhbSwgICMgNSBwZWRpZ3JlZSBzdHJlYW1zDQogICAgICB5ZWFyLCAgIyBzYW1wbGUgeWVhciAoYWthIGRlYXRoIHllYXIgZm9yIEZSQU56KQ0KICAgICAgb3JpZ2luLCAgIyBoYXRjaGVyeSBvciB3aWxkDQogICAgICAjIGhhdGNoZXJ5LCAgIyB3aGljaCBoYXRjaGVyeQ0KICAgICAgc2V4LCAgIyBtYWxlLCBmZW1hbGUsIHVua25vd24NCiAgICAgIGRhdGUsICAjIHNhbXBsZSBkYXRlDQogICAgICBET1ksICAjIGRheSBvZiB5ZWFyIChha2EganVsaWFuIHNhbXBsZSBkYXRlKQ0KICAgICAgbGVuZ3RoLCAgIyBtaWRleWUgdG8gaHlwdXJhbCBwbGF0ZSBsZW5ndGggaW4gbW0NCiAgICAgICMgaW50ZXJ0aWRhbCwgICMgc2FtcGxpbmcgbG9jYXRpb24gYWJvdmUgb3IgYmVsb3cgaW50ZXJ0aWRhbA0KICAgICAgIyBkaXN0YW5jZV9tb3V0aCwgICMgcml2ZXJkaXN0IGFwcHJveGltYXRlIHNhbXBsaW5nIGxvY2F0aW9uIGZyb20gbW91dGggaW4gbWV0ZXJzIA0KICAgICAgc3Bhd25pbmdfc3RhdGUsICAjIGFsaXZlLCBwaW5rIGdpbGwsIGdyZXkgZ2lsbCwgcm90dGluZyAoYWthIGluZGV4IG9mIHN0cmVhbSBsaWZlKQ0KICAgICAgIyBwcmVfc3Bhd24sICAjIFRSVUUvRkFMU0UgKGFkZGVkIGluIDIwMTYpOyBpZiB0aGUgZWdncyBhbmQgbWlsdCBkbyBub3QgZWFzaWx5IHJlbGVhc2UgYW5kIGNhcmNhc3MgaGFzIGNvbXBsZXRlbHkgZnVsbCBnb25hZHMNCiAgICAgICMgcGFydGlhbF9zcGF3biwgICMgVFJVRS9GQUxTRSAoYWRkZWQgaW4gMjAxNSk7IGZpc2ggaXMgc2FtcGxlZCAoZGVhZCBvciBhbGl2ZSkgd2l0aCBtb3JlIHRoYW4gYSBsaXR0bGUgZWdncyBvciBtaWx0DQogICAgICAjIHByZXllZF91cG9uLCAgIyBUUlVFL0ZBTFNFIChhZGRlZCBpbiAyMDE1KTsgZ29uYWRzIHJlbW92ZWQgYnkgcHJlZGF0b3JzDQogICAgICAjIHN0cmVhbV90cmliLCAgIyBzdHJlYW0gdHJpYnV0YXJ5IChHaWxtb3VyLCBQYWRkeSwgYW5kIFN0b2NrZGFsZSBoYXZlIG11bHRpcGxlKQ0KICAgICAgIyBsYXRpdHVkZSwgICMgc2FtcGxpbmcgYXJlYSAod2hlcmUgZmlzaCB3ZXJlIG1vdmVkIHRvIHByaW9yIHRvIHNhbXBsaW5nLCByb3VnaCBhcHByb3hpbWF0ZSBvZiBzcGF3bmluZyBsb2NhdGlvbikNCiAgICAgICMgbG9uZ2l0dWRlLCAgIyBzYW1wbGluZyBhcmVhICh3aGVyZSBmaXNoIHdlcmUgbW92ZWQgdG8gcHJpb3IgdG8gc2FtcGxpbmcsIHJvdWdoIGFwcHJveGltYXRlIG9mIHNwYXduaW5nIGxvY2F0aW9uKQ0KICAgICAgb3RvbGl0aF9tYXJrX3ByZXNlbnQsICAjIG90b2xpdGggbWFyayBwcmVzZW50ICh5ZXMgPSBoYXRjaGVyeSwgbm8gPSBuYXR1cmFsKQ0KICAgICAgb3RvbGl0aF9tYXJrX2lkLCAgIyBvdG9saXRoIGhhdGNoZXJ5IG1hcmsNCiAgICAgIG90b2xpdGhfbWFya19zdGF0dXNfY29kZSwgICMgb3RvbGl0aCByZWFkIHN0YXR1cyBjb2RlIChyZWFzb24gZm9yIG5vIHJlYWQgPSBvdmVyZ3JvdW5kLCBsb3N0LCBldGMuKQ0KICAgICAgc2lsbHksICAjIEdDTCBjb2xsZWN0aW9uIGNvZGUNCiAgICAgIGZpc2hfaWQsICAjIEdDTCBmaXNoIElEIHdpdGhpbiBhIHNpbGx5DQogICAgICBkbmFfdHJheV9jb2RlLCAgIyAxMC1kaWdpdCBEV1AgYmFyY29kZQ0KICAgICAgZG5hX3RyYXlfd2VsbF9jb2RlLCAgIyA0OCBEV1AgcG9zaXRpb24gMTo0OA0KICAgICAgIyBkbmFfdHJheV93ZWxsX3BvcywgICMgNDggRFdQIHBvc2l0aW9uIEExOkg2DQogICAgICAjIGRpc3RhbmNlX3RpZGUsICAjIHJpdmVyZGlzdCBhcHByb3hpbWF0ZSBzYW1wbGluZyBsb2NhdGlvbiBmcm9tIG1heGltdW0gaGlnaCB0aWRlDQogICAgICAjIHJpdmVyZGlzdF9zZWcsICAjIHJpdmVyZGlzdCByaXZlciBzZWdtZW50DQogICAgICAjIHJpdmVyZGlzdF92ZXJ0LCAgIyByaXZlcmRpc3QgdmVydGV4DQogICAgICAjIHJpdmVyZGlzdF9zbmFwZGlzdCwgICMgcml2ZXJkaXN0IHNuYXBwaW5nIGRpc3RhbmNlIChha2EgaG93IGZhciB3YXMgc2FtcGxpbmcgbG9jYXRpb24gbGF0L2xvbmcgZnJvbSB0aGUgc3RyZWFtIHBvbHlsaW5lKQ0KICAgICAgIyBoaWdoX3RpZGUgICMgaGlnaGVzdCBleHRlbnQgb2YgaGlnaCB0aWRlIGluIG1ldGVycyBmcm9tIG1vdXRoLCB1bmlxdWUgdG8gZWFjaCBzdHJlYW0NCiAgICAgIGZ3X2FnZSwNCiAgICAgIHN3X2FnZQ0KICAgICkgIA0KKSAgICAgICANCmBgYA0KDQojIFNhbXBsZSBTaXplcw0KDQpWZXJpZnkgdGhhdCBhbGwgZ2Vub3R5cGVkIHNhbXBsZXMgaGF2ZSBtZXRhZGF0YS4NCmBgYHtyfQ0KZ2Vub19zaWxseV9zb3VyY2UgPC0gc2FwcGx5KHNpbGx5dmVjLCBmdW5jdGlvbihzaWxseSkge2dldChwYXN0ZTAoc2lsbHksICIuZ2NsIikpJFNpbGx5U291cmNlfSkgJT4lIHVubGlzdCgpDQoNCnRhYmxlKGdlbm9fc2lsbHlfc291cmNlICVpbiUgbWV0YWRhdGEkc2lsbHlfc291cmNlKQ0KYGBgDQoNClNob290LCB3aGljaCBzYW1wbGVzIGRvIG5vdCBoYXZlIGFueSBtZXRhZGF0YT8NCg0KQWgsIEkgYmV0IHRoZXkgYXJlIHRoZSBGaXNoIENyZWVrIHRhZyBzYW1wbGVzIGZyb20gMjAxMyB0aGF0IFR5bGVyIERhbm4gYW5kIEhlYXRoZXIgTGlsbGVyIGNvbGxlY3RlZCENCmBgYHtyfQ0Kc2V0ZGlmZihnZW5vX3NpbGx5X3NvdXJjZSwgbWV0YWRhdGEkc2lsbHlfc291cmNlKQ0KYGBgDQoNCkNvcnJlY3QuIEFkZCB0aGVzZSB0byBtZXRhZGF0YS4NCmBgYHtyfQ0KbWV0YWRhdGEgPC0gDQogIGRwbHlyOjpiaW5kX3Jvd3MobWV0YWRhdGEsDQogICAgICAgICAgICAgICAgICAgQ01GSVNIQ1JUMTMuZ2NsICU+JSANCiAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoU0lMTFlfQ09ERSwgRktfRklTSF9JRCwgU2lsbHlTb3VyY2UsIEROQV9UUkFZX0NPREUsIEROQV9UUkFZX1dFTExfQ09ERSwgQ0FQVFVSRV9EQVRFKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNpbGx5ID0gU0lMTFlfQ09ERSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlzaF9pZCA9IEZLX0ZJU0hfSUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpbGx5X3NvdXJjZSA9IFNpbGx5U291cmNlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG5hX3RyYXlfY29kZSA9IEROQV9UUkFZX0NPREUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRuYV90cmF5X3dlbGxfY29kZSA9IEROQV9UUkFZX1dFTExfQ09ERSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZSA9IENBUFRVUkVfREFURSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgdGlkeXI6OnVuaXRlKGNvbCA9ICJzYW1wbGUiLCBkbmFfdHJheV9jb2RlOmRuYV90cmF5X3dlbGxfY29kZSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgdGlkeXI6OnVuaXRlKGNvbCA9ICJzaWxseV9zb3VyY2UiLCBjKHNpbGx5LCBmaXNoX2lkKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgZHBseXI6Om11dGF0ZShmcmFuel9pZCA9IHN0cmluZ3I6OnN0cl9jKHN0cmluZ3I6OnN0cl9zdWIoc2lsbHksIDEsIDIpLCAgIyAxc3QgbGV0dGVyIG9mIHNwZWNpZXMgbmFtZSwgMXN0IGxldHRlciBvZiBzdHJlYW0gbmFtZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl9zdWIoc2lsbHksIC0yLCAtMSksICAjIDIgZGlnaXQgc2FtcGxlIHllYXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3BhZChmaXNoX2lkLCB3aWR0aCA9IDUsIHBhZCA9ICIwIiwgc2lkZSA9ICJsZWZ0IikpLCAgIyA1IGRpZ2l0IGZpc2hpZCB1c2luZyBwYWRkZWQgemVyb3MgdXAgZnJvbnQpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGUgPSBsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZGF0ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmVhbSA9ICJGaXNoIC0gRG91Z2xhcyBJc2xhbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBET1kgPSBsdWJyaWRhdGU6OnlkYXkoZGF0ZSkgICMgZGF5IG9mIHllYXINCiAgICAgICAgICAgICAgICAgICAgICkgDQopDQpgYGANCg0KVmVyaWZ5IGFsbCBzYW1wbGVzIGhhdmUgbWV0YWRhdGEuDQpgYGB7cn0NCnRhYmxlKGdlbm9fc2lsbHlfc291cmNlICVpbiUgbWV0YWRhdGEkc2lsbHlfc291cmNlKQ0KYGBgDQoNCkV4Y2VsbGVudCwgYWxsIHByZXNlbnQgYW5kIGFjY291bnRlZCBmb3IuDQoNClJlY29yZCB3aGljaCBzYW1wbGVzIHdlcmUgZ2Vub3R5cGVkLg0KYGBge3J9DQptZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgDQogIGRwbHlyOjptdXRhdGUoZ2Vub3R5cGVkID0gc2lsbHlfc291cmNlICVpbiUgZ2Vub19zaWxseV9zb3VyY2UpDQpgYGANCg0KSG93IG1hbnkgc2FtcGxlcyBwZXIgY29sbGVjdGlvbiAoc2lsbHkpPw0KYGBge3J9DQptZXRhZGF0YSAlPiUgDQogIGRwbHlyOjpjb3VudChnZW5vdHlwZWQsIHNpbGx5KSAlPiUgDQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZ2Vub3R5cGVkLCB2YWx1ZXNfZnJvbSA9IG4pDQpgYGANCg0KIyMgQ2hlY2sgRm9yIFNhbXBsZSBEdXBsaWNhdGVzDQoNCkkgZ290IGEgd2VpcmQgbWFueS10by1tYW55IGVycm9yIGluIGEgam9pbiB0aGUgZmlyc3QgdGltZSBJIHJhbiB0aHJvdWdoIHRoaXMgc2NyaXB0LCBzbyBJIHdhbnQgdG8gY2hlY2sgYW5kIHNlZSBpZiB0aGVyZSBhcmUgYWN0dWFsbHkgZHVwbGljYXRlIHNhbXBsZXMgcHJlc2VudCBhbmQgaWYgc28sIHJlbW92ZSB0aGVtIQ0KYGBge3J9DQojIGJpbmQgYWxsIHNhbXBsZXMgdG9nZXRoZXIgaW50byBhIHNpbmdsZSAuZ2NsIG9iamVjdA0KbXkuZ2NsIDwtIHNhcHBseShzaWxseXZlYywgZnVuY3Rpb24oc2lsbHkpIHsNCiAgbXkuc2lsbHkgPC0gZ2V0KHBhc3RlMChzaWxseSwgIi5nY2wiKSkNCn0sIHNpbXBsaWZ5ID0gRkFMU0UpICU+JSANCiAgZHBseXI6OmJpbmRfcm93cygpDQpgYGANCg0KQW55IGR1cGxpY2F0ZSBgU2lsbHlTb3VyY2VgPw0KYGBge3J9DQp0YWJsZSh0YWJsZShteS5nY2wkU2lsbHlTb3VyY2UpKQ0KYGBgDQoNCkNsZWFuIHVwDQpgYGB7cn0NCnJtKG15LmdjbCkNCmBgYA0KDQpBcmUgdGhlcmUgbWV0YWRhdGEgZHVwbGljYXRlcz8NCmBgYHtyfQ0KdGFibGUodGFibGUobWV0YWRhdGEkc2lsbHlfc291cmNlKSkNCmBgYA0KDQpXZWxsIHNvbiBvZiBhIGJpc2N1aXQsIHRoZXJlIGFyZSENCmBgYHtyfQ0KbWV0YWRhdGEgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGR1cGxpY2F0ZWQoc2lsbHlfc291cmNlKSB8IGR1cGxpY2F0ZWQoc2lsbHlfc291cmNlLCBmcm9tTGFzdCA9IFRSVUUpKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKHNpbGx5X3NvdXJjZSkNCmBgYA0KDQpPa2F5LCB0aGV5IGFwcGVhciB0byBiZSB0cnVlIGR1cGxpY2F0ZXMsIG5vIGNvbmZsaWN0aW5nIGNvbHVtbnMuIFJlbW92ZSBkdXBsaWNhdGUgcm93cy4NCmBgYHtyfQ0KbWV0YWRhdGEgPC0gbWV0YWRhdGEgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3Qoc2lsbHlfc291cmNlLCAua2VlcF9hbGwgPSBUUlVFKQ0KYGBgDQoNCiMjIEpvaW4gTWV0YWRhdGENCg0KSm9pbiBgbWV0YWRhdGFgIHRvIGVhY2ggc2lsbHkgYnkgYFNpbGx5U291cmNlYC4NCmBgYHtyfQ0KeCA8LSBzYXBwbHkoc2lsbHl2ZWMsIGZ1bmN0aW9uKHNpbGx5KSB7DQogIG15LnNpbGx5IDwtIGdldChwYXN0ZTAoc2lsbHksICIuZ2NsIikpDQogIG15LnNpbGx5IDwtIG15LnNpbGx5ICU+JSBkcGx5cjo6bGVmdF9qb2luKA0KICAgIHkgPSBtZXRhZGF0YSAlPiUgZHBseXI6OnNlbGVjdCgtZ2Vub3R5cGVkKSwNCiAgICBieSA9IGRwbHlyOjpqb2luX2J5KFNpbGx5U291cmNlID09IHNpbGx5X3NvdXJjZSkNCiAgKQ0KICBhc3NpZ24oeCA9IHBhc3RlMChzaWxseSwgIi5nY2wiKSwNCiAgICAgICAgIHZhbHVlID0gbXkuc2lsbHksDQogICAgICAgICBwb3MgPSAxKQ0KfSkNCnJtKHgpICAjIHN1cHByZXNzZXMgb3V0cHV0DQpgYGANCg0KIyBGaWx0ZXIgUUMgQ3Jvc3NjaGVjayBQbGF0ZXMNCg0KQ3JlYXRlIGEgdmVjdG9yIG9mIHBsYXRlIElEcyB0byB0b3NzLCBwZW5kaW5nIFFDIGNyb3NzY2hlY2tzLg0KYGBge3J9DQpwbGF0ZV9pZHNfcWN4Y2hlY2sgPC0gYyg2NDQ2MSwgNjQ0NjIsIDY0NDgwLCA2NDQ4MiwgNjQ0ODMsIDY0NDg0LCA2NDQ4NSwgNjQ0ODYsIDY0NTAyLCA2NDUwMywgNjQ1MDksIDY0NTExLCA2NDUxMiwgNjQ1MTMsIDY0NTE0LCA2NDUxNSwgNjQ1NDIsIDY0NTQzLCA2NDU4MiwgNTYzNjIsIDU2MzYzKSAlPiUgYXMuY2hhcmFjdGVyKCkNCmBgYA0KDQpHZXQgc2FtcGxlIHNpemUgYnkgc2lsbHkuDQpgYGB7cn0NCigNCiAgc2FtcGxlX3NpemVfcWN4Y2hlY2sgPC0gZHBseXI6OnRpYmJsZShzaWxseSA9IHNpbGx5dmVjKSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKGdlbm90eXBlZCA9IEdDTHI6OnNpbGx5X24oc2lsbHl2ZWMgPSBzaWxseXZlYykgJT4lIGRwbHlyOjpwdWxsKG4pKQ0KKQ0KYGBgDQoNClJlbW92ZSBhbnkgZmlzaCBvbiB0aGUgcGxhdGVzIHBlbmRpbmcgUUMgY3Jvc3NjaGVja3MuDQpgYGB7cn0NCnggPC0gc2FwcGx5KHNpbGx5dmVjLCBmdW5jdGlvbihzaWxseSkgew0KICBteS5zaWxseSA8LSBnZXQocGFzdGUwKHNpbGx5LCAiLmdjbCIpKQ0KICBteS5zaWxseSA8LSBteS5zaWxseSAlPiUgZHBseXI6OmZpbHRlcighc3RyaW5ncjo6c3RyX2RldGVjdChzdHJpbmcgPSBQTEFURV9JRCwgcGF0dGVybiA9IHN0cmluZ3I6OnN0cl9jKHBsYXRlX2lkc19xY3hjaGVjaywgY29sbGFwc2UgPSAifCIpKSkNCiAgYXNzaWduKHggPSBwYXN0ZTAoc2lsbHksICIuZ2NsIiksDQogICAgICAgICB2YWx1ZSA9IG15LnNpbGx5LA0KICAgICAgICAgcG9zID0gMSkNCn0pDQpybSh4KSAgIyBzdXBwcmVzc2VzIG91dHB1dA0KYGBgDQoNCkhvdyBtYW55IGZpc2ggd2VyZSByZW1vdmVkIGJ5IHNpbGx5Pw0KYGBge3J9DQooDQogIHNhbXBsZV9zaXplX3FjeGNoZWNrIDwtIHNhbXBsZV9zaXplX3FjeGNoZWNrICU+JQ0KICAgIGRwbHlyOjptdXRhdGUocWN4Y2hlY2sgPSBnZW5vdHlwZWQgLSBHQ0xyOjpzaWxseV9uKHNpbGx5dmVjID0gc2lsbHl2ZWMpICU+JSBkcGx5cjo6cHVsbChuKSkNCikNCmBgYA0KDQpIb3cgbWFueSB0b3RhbCBzYW1wbGVzPw0KYGBge3J9DQpzdW0oc2FtcGxlX3NpemVfcWN4Y2hlY2skcWN4Y2hlY2spDQpgYGANCg0KT2theSwgdGhhdCBsb29rcyBhYm91dCByaWdodCBmb3IgMjEgcGxhdGVzLiBNdXN0IGhhdmUgYmVlbiBzb21lIHBhcnRpYWwgcGxhdGVzLg0KDQojIEdlbm90eXBlIFFBDQoNClN0YW5kYXJkIGRhdGEgUUEgKEdULXNlcSwgbm8gYGNvblNjb3JlYCk6DQoNCiAgKiBSZW1vdmUgZmlzaCBtaXNzaW5nIGdlbm90eXBlcyAoKio8ODAlKiogbG9jaSkNCiAgKiBSZW1vdmUgY29udGFtaW5hdGlvbiAoaGV0ZXJvenlnb3NpdHkgb3V0bGllcnMgKiorLy0gMy41KiogbW9kaWZpZWQgei1zY29yZSkNCiAgKiBSZW1vdmUgZHVwbGljYXRlcyAoKio+OTUlKiogbG9jaSBjb25jb3JkYW5jZSwgcmVtb3ZlICoqYm90aCoqIGR1cGxpY2F0ZXMpDQoNCmBgYHtyfQ0KKA0KICBzYW1wbGVfc2l6ZV9xYSA8LSBkcGx5cjo6dGliYmxlKHNpbGx5ID0gc2lsbHl2ZWMpICU+JQ0KICAgIGRwbHlyOjptdXRhdGUoZ2Vub3R5cGVkID0gR0NMcjo6c2lsbHlfbihzaWxseXZlYyA9IHNpbGx5dmVjKSAlPiUgZHBseXI6OnB1bGwobikpDQopDQpgYGANCg0KIyMgTWlzc2luZyBMb2NpICg8ODAlKQ0KDQpSZW1vdmUgZmlzaCB3aXRoIDw4MCUgbG9jaSBnZW5vdHlwZWQuDQpgYGB7cn0NCm1pc3NfbG9jaSA8LQ0KICBHQ0xyOjpyZW1vdmVfaW5kX21pc3NfbG9jaShzaWxseXZlYyA9IHNpbGx5dmVjLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wb3J0aW9uID0gMC44KQ0KDQojIG1pc3NfbG9jaSRJRHNfUmVtb3ZlZA0KDQpHQ0xyOjpzYXZlX29iamVjdHMob2JqZWN0cyA9ICJtaXNzX2xvY2kiLCBwYXRoID0gICIuLi9vYmplY3RzIiwgcmRzID0gVFJVRSkNCg0KKA0KICBzYW1wbGVfc2l6ZV9xYSA8LSBzYW1wbGVfc2l6ZV9xYSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKG1pc3NpbmcgPSBnZW5vdHlwZWQgLSBHQ0xyOjpzaWxseV9uKHNpbGx5dmVjID0gc2lsbHl2ZWMpICU+JSBkcGx5cjo6cHVsbChuKSkNCikNCmBgYA0KDQpPdmVyYWxsIGByIGZvcm1hdChzdW0oc2FtcGxlX3NpemVfcWEkbWlzc2luZyksIGJpZy5tYXJrID0gIiwiKWAgc2FtcGxlcyBkcm9wcGVkIGR1ZSB0byBwb29yIHF1YWxpdHkgRE5BLiANCg0KV2hhdCB3YXMgZ2Vub3R5cGluZyBzdWNjZXNzIGJ5IHNwYXduaW5nIHN0YXRlPw0KYGBge3J9DQptaXNzX2xvY2lfdGliIDwtIHRpYmJsZTo6ZW5mcmFtZShtaXNzX2xvY2kkSURzX1JlbW92ZWQsIG5hbWUgPSAic2lsbHkiLCB2YWx1ZSA9ICJmaXNoX2lkIikgJT4lIA0KICB0aWR5cjo6dW5uZXN0X2xvbmdlcihmaXNoX2lkKSAlPiUgDQogIGRwbHlyOjpsZWZ0X2pvaW4oeSA9IG1ldGFkYXRhLCBieSA9IGRwbHlyOjpqb2luX2J5KHNpbGx5LCBmaXNoX2lkKSkNCg0KbWV0YWRhdGEgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGdlbm90eXBlZCkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGRyb3BfbWlzcyA9IHNpbGx5X3NvdXJjZSAlaW4lIG1pc3NfbG9jaV90aWIkc2lsbHlfc291cmNlKSAlPiUgDQogIGRwbHlyOjpjb3VudChkcm9wX21pc3MsIHNwYXduaW5nX3N0YXRlKSAlPiUgDQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZHJvcF9taXNzLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgZHBseXI6OnJlbmFtZShuX2Ryb3AgPSBgVFJVRWAsIG5fa2VlcCA9IGBGQUxTRWApICU+JSANCiAgZHBseXI6Om11dGF0ZShuID0gbl9rZWVwICsgbl9kcm9wLA0KICAgICAgICAgICAgICAgIHBfa2VlcCA9IG5fa2VlcCAvIG4sDQogICAgICAgICAgICAgICAgc3Bhd25pbmdfc3RhdGUgPSBmYWN0b3Ioc3Bhd25pbmdfc3RhdGUsIGxldmVscyA9IGMoIkFsaXZlIiwgIlBpbmsgR2lsbCIsICJHcmV5IEdpbGwiLCAiUm90dGluZyIpKSkgJT4lIA0KICBkcGx5cjo6YXJyYW5nZShzcGF3bmluZ19zdGF0ZSkNCmBgYA0KDQpBcyBleHBlY3RlZCwgZ2Vub3R5cGluZyBzdWNjZXNzIHdhcyBtdWNoIGxvd2VyIGZvciByb3R0aW5nIGZpc2guDQoNCiMjIENvbnRhbWluYXRpb24gKEhldGVyb3p5Z29zaXR5ID4gKy8tIDMuNSBtb2RpZmllZCBaLXNjb3JlKQ0KDQpSZW1vdmUgY29udGFtaW5hdGVkIHNhbXBsZXMgcHJpb3IgdG8gY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZXMhDQoNCkluZGl2aWR1YWwgaGV0ZXJvenlnb3NpdHkgb3V0bGllcnMgaXMgb3VyIGJlc3QgbWV0cmljIGZvciByZW1vdmluZyBjb250YW1pbmF0ZWQgc2FtcGxlcyBpbiB0aGUgYWJzZW5jZSBvZiBzb21ldGhpbmcgbW9yZSBzb3BoaXN0aWNhdGVkIGxpa2UgKmNvblNjb3JlKiBmcm9tIFtHVHNjb3JlXShodHRwczovL2dpdGh1Yi5jb20vZ2ptY2tpbm5leS9HVHNjb3JlP3RhYj1yZWFkbWUtb3YtZmlsZSNzYW1wbGUtc3VtbWFyaWVzKS4gSW4gcHJldmlvdXMgYW5hbHlzZXMsIGluY2x1ZGluZyBbU2hlZGQgZXQgYWwuIDIwMjJdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTExL2V2YS4xMzM1NiksIHdlJ3ZlIHVzZWQgdGhlIHN0YW5kYXJkIDEuNSBJUVIgb3V0bGllciBkZXRlY3Rpb24gbWV0aG9kIHRvIHJlbW92ZSBleGNlc3NpdmVseSBoZXRlcm96eWdvdXMgaW5kaXZpZHVhbHMuIEhvd2V2ZXIsIHRoZSAxLjUgSVFSIG1ldGhvZCBhc3N1bWVzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgYnV0IGhlYXJ0IHNhbXBsZSBoZXRlcm96eWdvc2l0aWVzIHRlbmQgdG8gYmUgc2tld2VkIHJpZ2h0IGR1ZSB0byBjb250YW1pbmF0aW9uLiBBcyBvZiAyMDI0LTAyLTA4LCB3ZSBkZWNpZGVkIHRvIHBpdm90IHRvIHVzaW5nIFttb2RpZmllZCBaLXNjb3Jlc10oaHR0cHM6Ly93d3cuaXRsLm5pc3QuZ292L2Rpdjg5OC9oYW5kYm9vay9lZGEvc2VjdGlvbjMvZWRhMzVoLmh0bSkgd2l0aCBjdXRvZmZzIG9mICsvLSAzLjUgYXMgcmVjb21tZW5kZWQgYnkgSWdsZXdpY3ogYW5kIEhvYWdsaW4gW14xXS4gVGhlIG1vZGlmaWVkIFotc2NvcmUgdXNlcyB0aGUgbWVkaWFuIGFuZCBtZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChNQUQpIGluc3RlYWQgb2YgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIGlzIHRodXMgbW9yZSByb2J1c3QgdG8gb3V0bGllcnMgYW5kIGFzeW1tZXRyaWNhbCBkaXN0cmlidXRpb25zLg0KDQpbXjFdOiBCb3JpcyBJZ2xld2ljeiBhbmQgRGF2aWQgSG9hZ2xpbiAoMTk5MyksICJWb2x1bWUgMTY6IEhvdyB0byBEZXRlY3QgYW5kIEhhbmRsZSBPdXRsaWVycyIsIFRoZSBBU1FDIEJhc2ljIFJlZmVyZW5jZXMgaW4gUXVhbGl0eSBDb250cm9sOiBTdGF0aXN0aWNhbCBUZWNobmlxdWVzLCBFZHdhcmQgRi4gTXlreXRrYSwgUGguRC4sIEVkaXRvci4gDQoNClNvdXJjZSBmdW5jdGlvbiBhbmQgY2FsY3VsYXRlIGluZGl2aWR1YWwgaGV0ZXJvenlnb3NpdGllcyBmcm9tIGBBSFJQLVBXUy1QaW5rLVNhbG1vbi1QZWRpZ3JlZXNgIEdpdEh1YiByZXBvLg0KYGBge3J9DQpzb3VyY2UoIn4vR2l0SHViX3JlcG9zL0FIUlAtUFdTLVBpbmstU2FsbW9uLVBlZGlncmVlcy9jb2RlL2Z1bmN0aW9ucy9jYWxjX2luZF9oZXQuUiIpDQoNCigNCiAgaW5kX2hldCA8LQ0KICAgIGNhbGNfaW5kX2hldCgNCiAgICAgIHNpbGx5dmVjID0gc2lsbHl2ZWMsDQogICAgICBsb2NpID0gbG9jaTI1MSwNCiAgICAgIG5jb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gNA0KICAgICkNCikNCmBgYA0KDQpDYWxjdWxhdGUgKy8tIDMuNSBtb2RpZmllZCB6LXNjb3JlIGN1dG9mZnMgYnkgKnN0cmVhbSouIA0KKipOT1RFKiogdGhpcyBtZWFucyB0aGF0IHRoZSBleGFjdCBoZXRlcm96eWdvc2l0eSBjdXRvZmYgd2lsbCB2YXJ5IHNsaWdodGx5IGJ5IHN0cmVhbS4gRm9yIFBXUyBQaW5rIFNhbG1vbiwgSSBkaWQgaXQgc2VwYXJhdGVseSBieSBzaWxseSwgYnV0IHNvbWUgb2Ygb3VyIHNhbXBsZSBzaXplcyBhcmUgcHJldHR5IHNtYWxsIGhlcmUgYW5kIHdlIGhhdmUgdGhlIHNlcGFyYXRlICJ0YWciIHNpbGx5cy4gSSdtIG9wdGluZyB0byBkbyB0aGlzIGJ5IHN0cmVhbSB0byBhbGxvdyBmb3Igc3VidGxlIGNoYW5nZXMgaW4gYWxsZWxlIGZyZXF1ZW5jeSBwZXIgc3RyZWFtLg0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBtZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChNQUQpDQptYWQgPC0gZnVuY3Rpb24oeCkgew0KICBtZWRpYW4oYWJzKHggLSBtZWRpYW4oeCkpKQ0KfQ0KDQojIENhbGN1bGF0ZSB0aGUgbW9kaWZpZWQgei1zY29yZSBmb3IgZWFjaCBpbmRpdmlkdWFsDQooDQogIGluZF9oZXQgPC0gaW5kX2hldCAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKA0KICAgICAgeWVhciA9IDIwMDAgKyBhcy5udW1lcmljKHN0cmluZ3I6OnN0cl9zdWIoDQogICAgICAgIHN0cmluZyA9IHNpbGx5LA0KICAgICAgICBzdGFydCA9IC0yLA0KICAgICAgICBlbmQgPSAtMQ0KICAgICAgKSksDQogICAgICBzdHJlYW0gPSBkcGx5cjo6Y2FzZV93aGVuKA0KICAgICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KHN0cmluZyA9IHNpbGx5LCBwYXR0ZXJuID0gIkFETSIpIH4gIkFkbWlyYWx0eSIsDQogICAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3Qoc3RyaW5nID0gc2lsbHksIHBhdHRlcm4gPSAiRklTSCIpIH4gIkZpc2giLA0KICAgICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KHN0cmluZyA9IHNpbGx5LCBwYXR0ZXJuID0gIlBST1MiKSB+ICJQcm9zcGVjdCIsDQogICAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3Qoc3RyaW5nID0gc2lsbHksIHBhdHRlcm4gPSAiU0FXIikgfiAiU2F3bWlsbCIsDQogICAgICAgIFRSVUUgfiAibWlzdGFrZXNfd2VyZV9tYWRlIg0KICAgICAgKQ0KICAgICkgJT4lDQogICAgZHBseXI6Omdyb3VwX2J5KHN0cmVhbSkgJT4lICAjIGN1dG9mZnMgYXJlIHNwZWNpZmljIHRvIGVhY2ggc3RyZWFtIQ0KICAgIGRwbHlyOjptdXRhdGUoDQogICAgICBtb2RpZmllZF96X3Njb3JlID0gMC42NzQ1ICogKGhldCAtIG1lZGlhbihoZXQpKSAvIG1hZChoZXQpLA0KICAgICAgb3V0bGllciA9IGRwbHlyOjpjYXNlX3doZW4oYWJzKG1vZGlmaWVkX3pfc2NvcmUpID4gMy41IH4gVFJVRSwgVFJVRSB+IEZBTFNFKQ0KICAgICkgJT4lDQogICAgZHBseXI6OnVuZ3JvdXAoKQ0KKQ0KICANCkdDTHI6OnNhdmVfb2JqZWN0cygiaW5kX2hldCIsIHBhdGggPSAib2JqZWN0cyIsIHJkcyA9IFRSVUUpDQpgYGANCg0KUGxvdCBkaXN0cmlidXRpb24gb2YgaGV0ZXJvenlnb3NpdHkgYW5kIHNob3cgb3V0bGllcnMuDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTEwfQ0KaW5kX2hldCAlPiUgDQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IGhldCwgZmlsbCA9IG91dGxpZXIpKSArDQogIGdncGxvdDI6Omdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSAvIGxlbmd0aChsb2NpMjUxKSkgKw0KICBnZ3Bsb3QyOjpmYWNldF9ncmlkKHJvd3MgPSBnZ3Bsb3QyOjp2YXJzKHN0cmVhbSksIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIGdncGxvdDI6OnhsaW0oMCwgMSkgKw0KICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIk91dGxpZXIiLCB2YWx1ZXMgPSBjKCJUUlVFIiA9ICJibGFjayIsICJGQUxTRSIgPSAiZ3JleTYwIikpICsNCiAgZ2dwbG90Mjo6eGxhYigiSW5kaXZpZHVhbCBIZXRlcm96eWdvc2l0eSIpICsNCiAgZ2dwbG90Mjo6eWxhYigiRnJlcXVlbmN5IikgKw0KICBnZ3Bsb3QyOjpnZ3RpdGxlKCJJbmRpdmlkdWFsIEhldGVyb3p5Z29zaXR5IC0gQnkgU3RyZWFtIikgKw0KICBnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkgDQpgYGANCg0KUmVtb3ZlIG91dGxpZXJzLg0KYGBge3J9DQpvdXRwdXQgPC0gbGFwcGx5KHNpbGx5dmVjLCBmdW5jdGlvbih4KSB7DQogIEdDTHI6OnJlbW92ZV9pZHMoDQogICAgc2lsbHkgPSB4LA0KICAgIElEcyA9IGluZF9oZXQgJT4lIGRwbHlyOjpmaWx0ZXIob3V0bGllciwgc2lsbHkgPT0geCkgJT4lIGRwbHlyOjpwdWxsKGZpc2hfaWQpDQogICkNCn0pDQoNCm1lc3NhZ2UocGFzdGUwKCJBIHRvdGFsIG9mICIsIGluZF9oZXQgJT4lIGRwbHlyOjpmaWx0ZXIob3V0bGllcikgJT4lIG5yb3coKSwgIiBpbmRpdmlkdWFscyB3ZXJlIHJlbW92ZWQgZnJvbSBzaWxseXMgaW4gc2lsbHl2ZWMuIikpDQpgYGANCg0KTm90IHRvbyBtYW55IGNvbnRhbWluYXRlZCBzYW1wbGVzISBNb3N0IGxpa2VseSBjdWxsZWQgZHVyaW5nIG1pc3NpbmcgbG9jaS4NCmBgYHtyfQ0KKA0KICBzYW1wbGVfc2l6ZV9xYSA8LSBzYW1wbGVfc2l6ZV9xYSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKGhldGVyb3p5Z29zaXR5ID0gZ2Vub3R5cGVkIC0gbWlzc2luZyAtIEdDTHI6OnNpbGx5X24oc2lsbHl2ZWMgPSBzaWxseXZlYykgJT4lIGRwbHlyOjpwdWxsKG4pKQ0KKQ0KYGBgDQoNCk92ZXJhbGwgYHIgZm9ybWF0KHN1bShzYW1wbGVfc2l6ZV9xYSRoZXRlcm96eWdvc2l0eSksIGJpZy5tYXJrID0gIiwiKWAgc2FtcGxlcyBkcm9wcGVkIGR1ZSB0byBzYW1wbGUgY29udGFtaW5hdGlvbi4gDQoNCiMjIER1cGxpY2F0ZSAoPj05NSUpDQoNCklkZW50aWZ5IHBvdGVudGlhbCBkdXBsaWNhdGUgZ2Vub3R5cGVzICg+PTk1JSBsb2NpKS4NCmBgYHtyfQ0KZHVwbGljYXRlX2NoZWNrXzk1IDwtDQogIEdDTHI6OmR1cGNoZWNrX3dpdGhpbl9zaWxseSgNCiAgICBzaWxseXZlYyA9IHNpbGx5dmVjLA0KICAgIG1pbnByb3BvcnRpb24gPSAwLjk1LA0KICAgIG1pbm5vbm1pc3NpbmcgPSAwLjYsDQogICAgbmNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkgLSA0DQogICkNCg0KR0NMcjo6c2F2ZV9vYmplY3RzKCJkdXBsaWNhdGVfY2hlY2tfOTUiLCBwYXRoID0gIm9iamVjdHMiLCByZHMgPSBUUlVFKQ0KYGBgDQoNCkhvdyBtYW55IGR1cGxpY2F0ZSBwYWlycyBieSBzaWxseT8NCmBgYHtyfQ0KZHVwbGljYXRlX2NoZWNrXzk1ICU+JSANCiAgZHBseXI6OmNvdW50KHNpbGx5KSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKGRwbHlyOjpkZXNjKG4pKQ0KYGBgDQoNClJlbW92ZSAqKmJvdGgqKiBkdXBsaWNhdGVzISBBcyBvcHBvc2VkIHRvIEdTSSB3b3JrLCB3aGVyZSB3ZSB3YW50IHRvIGtlZXAgaW5kaXZpZHVhbHMgYnV0IGFyZW7igJl0IHR5cGljYWxseSB3b3JyaWVkIGFib3V0IHBhaXJlZCBkYXRhLCBoZXJlIHdlIHdhbnQgdG8gcmVtb3ZlIGJvdGggaW5kaXZpZHVhbHMgYXMgdGhlIHBhaXJlZCBkYXRhIGludGVncml0eSAoaW5jbHVkaW5nIG90b2xpdGggcmVhZHMhKSBpcyBsb3N0Lg0KYGBge3J9DQpkdXBsaWNhdGVzX3JlbW92ZWQgPC0NCiAgR0NMcjo6cmVtb3ZlX2R1cHMoZHVwY2hlY2sgPSBkdXBsaWNhdGVfY2hlY2tfOTUsIHJlbW92ZV9ib3RoID0gVFJVRSkNCg0KR0NMcjo6c2F2ZV9vYmplY3RzKCJkdXBsaWNhdGVzX3JlbW92ZWQiLCBwYXRoID0gIm9iamVjdHMiLCByZHMgPSBUUlVFKQ0KDQooDQogIHNhbXBsZV9zaXplX3FhIDwtIHNhbXBsZV9zaXplX3FhICU+JQ0KICAgIGRwbHlyOjptdXRhdGUoDQogICAgICBkdXBsaWNhdGUgPSBnZW5vdHlwZWQgLSBtaXNzaW5nIC0gaGV0ZXJvenlnb3NpdHkgLSBHQ0xyOjpzaWxseV9uKHNpbGx5dmVjID0gc2lsbHl2ZWMpICU+JSBkcGx5cjo6cHVsbChuKQ0KICAgICkNCikNCmBgYA0KDQpPdmVyYWxsIGByIGZvcm1hdChzdW0oc2FtcGxlX3NpemVfcWEkZHVwbGljYXRlKSwgYmlnLm1hcmsgPSAiLCIpYCBzYW1wbGVzIGRyb3BwZWQgZHVlIHRvIGR1cGxpY2F0ZSBnZW5vdHlwZXMuIA0KDQojIyBGaW5hbA0KDQpGaW5hbCwgcG9zdC1RQSBzYW1wbGUgc2l6ZXMgYnkgc2lsbHkuDQpgYGB7cn0NCigNCiAgc2FtcGxlX3NpemVfcWEgPC0gc2FtcGxlX3NpemVfcWEgJT4lDQogICAgZHBseXI6Om11dGF0ZSgNCiAgICAgIGZpbmFsID0gIEdDTHI6OnNpbGx5X24oc2lsbHl2ZWMgPSBzaWxseXZlYykgJT4lIGRwbHlyOjpwdWxsKG4pDQogICAgKQ0KKQ0KDQpHQ0xyOjpzYXZlX29iamVjdHMoInNhbXBsZV9zaXplX3FhIiwgcGF0aCA9ICJvYmplY3RzIiwgcmRzID0gVFJVRSkNCg0KZnM6OmRpcl9jcmVhdGUoIm91dHB1dCIpDQpyZWFkcjo6d3JpdGVfY3N2KHggPSBzYW1wbGVfc2l6ZV9xYSwgZmlsZSA9ICJvdXRwdXQvc2FtcGxlX3NpemVfcWEuY3N2IikNCmBgYA0KDQojIFNhdmUNCg0KU2F2ZSBmaW5hbCBnZW5vdHlwZXMgYW5kIHVwZGF0ZSBtZXRhZGF0YS4NCg0KIyMgR2Vub3R5cGVzDQoNClNhdmUgcG9zdC1RQSBnZW5vdHlwZXMgd2l0aCBqb2luZWQgbWV0YWRhdGEuDQpgYGB7cn0NCmZzOjpkaXJfY3JlYXRlKCJkYXRhL2dlbm90eXBlcy9wb3N0UUEiKQ0KR0NMcjo6c2F2ZV9zaWxseXMoc2lsbHl2ZWMgPSBzaWxseXZlYywgcGF0aCA9ICJkYXRhL2dlbm90eXBlcy9wb3N0UUEiLCByZHMgPSBUUlVFKQ0KYGBgDQoNCiMjIE1ldGFkYXRhDQoNCmFkZCBgcGFzc19RQWANCmBgYHtyfQ0KcG9zdFFBX3NpbGx5X3NvdXJjZSA8LSBzYXBwbHkoc2lsbHl2ZWMsIGZ1bmN0aW9uKHNpbGx5KSB7Z2V0KHBhc3RlMChzaWxseSwgIi5nY2wiKSkkU2lsbHlTb3VyY2V9KSAlPiUgdW5saXN0KCkNCg0KbWV0YWRhdGEgPC0gbWV0YWRhdGEgJT4lIA0KICBkcGx5cjo6bXV0YXRlKHBhc3NfUUEgPSBzaWxseV9zb3VyY2UgJWluJSBwb3N0UUFfc2lsbHlfc291cmNlKQ0KYGBgDQoNCkNvbmZpcm0uDQpgYGB7cn0NCm1ldGFkYXRhICU+JSANCiAgZHBseXI6OmNvdW50KGdlbm90eXBlZCwgcGFzc19RQSkNCmBgYA0KDQoqKk5PVEUqKiB0aGF0IGBnZW5vdHlwZWRgIGluIGBzYW1wbGVfc2l6ZV9xYWAgZG9lc24ndCBtYXRjaCBgZ2Vub3R5cGVkYCBpbiBgbWV0YWRhdGFgIGJlY2F1c2UgSSBwcmUtZmlsdGVyZWQgb3V0IFFDIENyb3NzY2hlY2sgcGxhdGVzLg0KYGBge3J9DQpzYW1wbGVfc2l6ZV9xYSAlPiUgDQogIGphbml0b3I6OmFkb3JuX3RvdGFscygpICU+JSANCiAgZHBseXI6OmZpbHRlcihzaWxseSA9PSAiVG90YWwiKQ0KYGBgDQoNCiMgUUEgQ29uY2x1c2lvbnMNCg0KT3ZlcmFsbCBgciBmb3JtYXQoc3VtKG1ldGFkYXRhJGdlbm90eXBlZCksIGJpZy5tYXJrID0gIiwiKWAgdG90YWwgcGVkaWdyZWUgc2FtcGxlcyB3ZXJlIGdlbm90eXBlZDsgYHIgZm9ybWF0KHN1bShtZXRhZGF0YSRnZW5vdHlwZWQpIC0gc3VtKHNhbXBsZV9zaXplX3FhJGdlbm90eXBlZCksIGJpZy5tYXJrID0gIiwiKWAgd2VyZSByZW1vdmVkIGR1ZSB0byBwZW5kaW5nIFFDIENyb3NzY2hlY2tzOyBgciBmb3JtYXQoc3VtKHNhbXBsZV9zaXplX3FhJG1pc3NpbmcpLCBiaWcubWFyayA9ICIsIilgIHdlcmUgcmVtb3ZlZCBkdWUgdG8gcG9vciBxdWFsaXR5IEROQSAobWlzc2luZyA+PSAyMCUgbG9jaSk7IGByIGZvcm1hdChzdW0oc2FtcGxlX3NpemVfcWEkaGV0ZXJvenlnb3NpdHkpLCBiaWcubWFyayA9ICIsIilgIHdlcmUgcmVtb3ZlZCBkdWUgdG8gY29udGFtaW5hdGVkIEROQSAoaGV0ZXJvenlnb3NpdHkgPj0gMy41IG1vZGlmaWVkIHotc2NvcmUpOyBhbmQgYHIgZm9ybWF0KHN1bShzYW1wbGVfc2l6ZV9xYSRkdXBsaWNhdGUpLCBiaWcubWFyayA9ICIsIilgIHdlcmUgcmVtb3ZlZCBkdWUgdG8gZHVwbGljYXRlIGdlbm90eXBlcyAoPj0gOTUlIGxvY2kpLCBsZWF2aW5nIGEgdG90YWwgb2YgKipgciBmb3JtYXQoc3VtKHNhbXBsZV9zaXplX3FhJGZpbmFsKSwgYmlnLm1hcmsgPSAiLCIpYCoqIGluIHRoZSBmaW5hbCBkYXRhc2V0Lg0KDQojIEFkYXB0aXZlIExvY2kgR2Vub3R5cGVzDQoNCkNyZWF0ZSBhIHNpbmdsZSBvYmplY3Qgd2l0aCBqdXN0IHRoZSBtZXRhZGF0YSBhbmQgYWRhcHRpdmUgbG9jaSBnZW5vdHlwZXMuDQpgYGB7cn0NCigNCiAgY2h1bV9hZGFwdGl2ZV9nZW5vIDwtIHNhcHBseShzaWxseXZlYywgZnVuY3Rpb24oc2lsbHkpIHsNCiAgICBnZXQocGFzdGUwKHNpbGx5LCAiLmdjbCIpKQ0KICB9LCBzaW1wbGlmeSA9IEZBTFNFKSAlPiUNCiAgICBkcGx5cjo6YmluZF9yb3dzKCkgJT4lDQogICAgZHBseXI6OnNlbGVjdCgNCiAgICAgIHNpbGx5X3NvdXJjZSA9IFNpbGx5U291cmNlLA0KICAgICAgcGxhdGVfaWQgPSBQTEFURV9JRCwNCiAgICAgIHN0cmVhbTpzd19hZ2UsDQogICAgICBkcGx5cjo6Y29udGFpbnMoIk9rZVYyIikNCiAgICApDQopDQpgYGANCg0KIyMgQ2FsY3VsYXRlIEFsbGVsZSBGcmVxdWVuY2llcw0KDQpDYWxjdWxhdGUgdGhlIGFsbGVsZSBjb3VudHMgZm9yIGVhY2ggbG9jdXMgYnkgc3RyZWFtLCB5ZWFyLCBhbmQgRE9ZLg0KQWRhcHRlZCBmcm9tIFBhdCBCYXJyeSdzIGNvZGUgaW4gYEFIUlBfQ2h1bUFkYXB0aXZlLmh0bWxgDQpgYGB7cn0NCmxvY2lfYWRhcHRpdmUgPC0gZ3JlcChwYXR0ZXJuID0gIk9rZVYyIiwgeCA9IGxvY2kyNTEsIHZhbHVlID0gVFJVRSkNCg0KIyB0ZXN0aW5nDQojIGNodW1fYWRhcHRpdmVfZ2VubyAlPiUNCiMgICBkcGx5cjo6c2VsZWN0KHNpbGx5X3NvdXJjZSwgc3RyZWFtLCB5ZWFyLCBET1ksIHRpZHlzZWxlY3Q6OnN0YXJ0c193aXRoKGxvY2lfYWRhcHRpdmVbMV0pKSAlPiUNCiMgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGNvbHMgPSB0aWR5c2VsZWN0OjpzdGFydHNfd2l0aChsb2NpX2FkYXB0aXZlWzFdKSwgbmFtZXNfdG8gPSAibG9jdXMiLCB2YWx1ZXNfdG8gPSAiYWxsZWxlIikgJT4lDQojICAgZHBseXI6Om11dGF0ZShhbGxlbGUgPSBmYWN0b3IoYWxsZWxlKSwNCiMgICAgICAgICAgICAgICAgIGFsbGVsZSA9IGFzLm51bWVyaWMoYWxsZWxlKSkgJT4lICAjIG1ha2UgZmFjdG9yIGFuZCBjb252ZXJ0IHRvIG51bWVyaWMNCiMgICBkcGx5cjo6Y291bnQoc3RyZWFtLCB5ZWFyLCBET1ksIGFsbGVsZSkgJT4lDQojICAgZHBseXI6Om11dGF0ZShsb2N1cyA9IGxvY2lfYWRhcHRpdmVbMV0pDQoNCiMgY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZWFjaCBhbGxlbGUgb2JzZXJ2ZWQgcGVyIHN0cmVhbSwgeWVhciwgZGF5DQooY2h1bV9hZGFwdGl2ZV9hbGxlbGVfY291bnRzIDwtIGxhcHBseShsb2NpX2FkYXB0aXZlLCBmdW5jdGlvbihsb2N1cykgew0KICBjaHVtX2FkYXB0aXZlX2dlbm8gJT4lDQogICAgZHBseXI6OnNlbGVjdChzaWxseV9zb3VyY2UsDQogICAgICAgICAgICAgICAgICBzdHJlYW0sDQogICAgICAgICAgICAgICAgICB5ZWFyLA0KICAgICAgICAgICAgICAgICAgRE9ZLA0KICAgICAgICAgICAgICAgICAgdGlkeXNlbGVjdDo6c3RhcnRzX3dpdGgobG9jdXMpKSAlPiUNCiAgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKA0KICAgICAgY29scyA9IHRpZHlzZWxlY3Q6OnN0YXJ0c193aXRoKGxvY3VzKSwNCiAgICAgIG5hbWVzX3RvID0gImxvY3VzIiwNCiAgICAgIHZhbHVlc190byA9ICJhbGxlbGUiDQogICAgKSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKGFsbGVsZSA9IGZhY3RvcihhbGxlbGUpLA0KICAgICAgICAgICAgICAgIGFsbGVsZSA9IGFzLm51bWVyaWMoYWxsZWxlKSkgJT4lICAjIG1ha2UgZmFjdG9yIGFuZCBjb252ZXJ0IHRvIG51bWVyaWMNCiAgICBkcGx5cjo6Y291bnQoc3RyZWFtLCB5ZWFyLCBET1ksIGFsbGVsZSkgJT4lDQogICAgZHBseXI6Om11dGF0ZShsb2N1cyA9IGxvY3VzKQ0KfSkgJT4lDQogICAgZHBseXI6OmJpbmRfcm93cygpKQ0KYGBgDQoNCkNvbnZlcnQgdG8gYWxlbGxlIGZyZXF1ZW5jaWVzLg0KYGBge3J9DQooDQogIGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXEgPC0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfY291bnRzICU+JQ0KICAgIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gYWxsZWxlLCB2YWx1ZXNfZnJvbSA9IG4pICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLWBOQWApICU+JSAgIyBkcm9wIG5vLWNhbGxzDQogICAgZHBseXI6Om11dGF0ZSgNCiAgICAgIGAxYCA9IHJlcGxhY2VfbmEoYDFgLCAwKSwNCiAgICAgICMgYWNjb3VudCBmb3IgdW5vYnNlcnZlZCBhbGxlbGVzIG9uIGEgZ2l2ZW4gZGF5DQogICAgICBgMmAgPSByZXBsYWNlX25hKGAyYCwgMCkNCiAgICApICU+JQ0KICAgIHRpZHlyOjpwaXZvdF9sb25nZXIoDQogICAgICBjb2xzID0gYDFgOmAyYCwNCiAgICAgIG5hbWVzX3RvID0gImFsbGVsZSIsDQogICAgICB2YWx1ZXNfdG8gPSAibiINCiAgICApICU+JQ0KICAgIGRwbHlyOjpncm91cF9ieShzdHJlYW0sIHllYXIsIERPWSwgbG9jdXMpICU+JQ0KICAgIGRwbHlyOjptdXRhdGUoDQogICAgICBuX2FsbGVsZXMgPSBzdW0obiksDQogICAgICBwX2FsbGVsZXMgPSBuIC8gbl9hbGxlbGVzLA0KICAgICAgU05QID0gcGFzdGUobG9jdXMsIGFsbGVsZSwgc2VwID0gIl8iKQ0KICAgICkgJT4lIA0KICAgIGRwbHlyOjp1bmdyb3VwKCkNCikNCmBgYA0KDQojIyBWaWV3IEFsbGVsZSBGcmVxdWVuY2llcw0KDQpgYGB7cn0NCiMgIFRPRE8NCiMgZG91YmxlIGNoZWNrIGFsbGVsZSBmcmVxdWVuY3kgY29kZSB0byBtYWtlIHN1cmUgaXQgaXMgY29ycmVjdA0KIyBtYWtlIHN1cmUgYWxsZWxlcyBhcmUgYmVpbmcgYXNzaWduZWQgMSB2cy4gMiBjb25zaXN0ZW50bHkNCiMgZmlsdGVyIG91dCBoYXRjaGVyeSBzdHJheXM/DQojIGNoZWNrIHNhbXBsZSBzaXplcyBieSBzdHJlYW0veWVhci9ET1k/DQojIHNlZSBpZiBzcGF3bmluZyBzdGF0ZSBjb3JyZWxhdGVzPw0KYGBgDQoNCiMjIyBBZG1pcmFsdHkNCg0KYGBge3IgZmlnLmhlaWdodD0xMH0NCmNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXEgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHN0cmVhbSA9PSAiQWRtaXJhbHR5IikgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGxvY3VzID0gc3RyaW5ncjo6c3RyX3JlbW92ZV9hbGwoc3RyaW5nID0gbG9jdXMsIHBhdHRlcm4gPSAiT2tlVjJfIikpICU+JSANCiAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gRE9ZLCB5ID0gcF9hbGxlbGVzLCBjb2xvdXIgPSBTTlAsIGdyb3VwX2J5KFNOUCkpKSArDQogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArDQogIGdncGxvdDI6Omdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHdlaWdodCA9ICJuX2FsbGVsZXMiLCBmb3JtdWxhID0geX54KSArDQogIGdncGxvdDI6OmZhY2V0X2dyaWQobG9jdXMgfiB5ZWFyKSArIA0KICBnZ3Bsb3QyOjp0aGVtZV9idygpICsgDQogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykNCmBgYA0KDQojIyMgRmlzaA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEwfQ0KY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc3RyZWFtID09ICJGaXNoIC0gRG91Z2xhcyBJc2xhbmQiKSAlPiUgDQogIGRwbHlyOjptdXRhdGUobG9jdXMgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlX2FsbChzdHJpbmcgPSBsb2N1cywgcGF0dGVybiA9ICJPa2VWMl8iKSkgJT4lIA0KICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSBET1ksIHkgPSBwX2FsbGVsZXMsIGNvbG91ciA9IFNOUCwgZ3JvdXBfYnkoU05QKSkpICsNCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsNCiAgZ2dwbG90Mjo6Z2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgd2VpZ2h0ID0gIm5fYWxsZWxlcyIsIGZvcm11bGEgPSB5fngpICsNCiAgZ2dwbG90Mjo6ZmFjZXRfZ3JpZChsb2N1cyB+IHllYXIpICsgDQogIGdncGxvdDI6OnRoZW1lX2J3KCkgKyANCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQ0KYGBgDQoNCiMjIyBQcm9zcGVjdA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEwfQ0KY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc3RyZWFtID09ICJQcm9zcGVjdCIpICU+JSANCiAgZHBseXI6Om11dGF0ZShsb2N1cyA9IHN0cmluZ3I6OnN0cl9yZW1vdmVfYWxsKHN0cmluZyA9IGxvY3VzLCBwYXR0ZXJuID0gIk9rZVYyXyIpKSAlPiUgDQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IERPWSwgeSA9IHBfYWxsZWxlcywgY29sb3VyID0gU05QLCBncm91cF9ieShTTlApKSkgKw0KICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKw0KICBnZ3Bsb3QyOjpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCB3ZWlnaHQgPSAibl9hbGxlbGVzIiwgZm9ybXVsYSA9IHl+eCkgKw0KICBnZ3Bsb3QyOjpmYWNldF9ncmlkKGxvY3VzIH4geWVhcikgKyANCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArIA0KICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpDQpgYGANCg0KIyMjIFNhd21pbGwNCg0KYGBge3IgZmlnLmhlaWdodD0xMH0NCmNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXEgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHN0cmVhbSA9PSAiU2F3bWlsbCIpICU+JSANCiAgZHBseXI6Om11dGF0ZShsb2N1cyA9IHN0cmluZ3I6OnN0cl9yZW1vdmVfYWxsKHN0cmluZyA9IGxvY3VzLCBwYXR0ZXJuID0gIk9rZVYyXyIpKSAlPiUgDQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IERPWSwgeSA9IHBfYWxsZWxlcywgY29sb3VyID0gU05QLCBncm91cF9ieShTTlApKSkgKw0KICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKw0KICBnZ3Bsb3QyOjpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCB3ZWlnaHQgPSAibl9hbGxlbGVzIiwgZm9ybXVsYSA9IHl+eCkgKw0KICBnZ3Bsb3QyOjpmYWNldF9ncmlkKGxvY3VzIH4geWVhcikgKyANCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArIA0KICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpDQpgYGANCg0KZW5k